﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style type="text/css"> /* <![CDATA[ */
  @import "branding/css/tigris.css";
  @import "branding/css/inst.css";
  /* ]]> */</style>
<link rel="stylesheet" type="text/css" media="print"
  href="branding/css/print.css" />
<title>Subversion开发入门</title>
</head>

<body>
<div class="app">

<h1 style="text-align: center;">Subversion开发入门</h1>

<p>如果你想给Subversion工程贡献代码, 请首先阅读本文档.</p>
<p>翻译者:litzhang sdl@yunchengonline.com</p>

<pre>
$LastChangedDate: 2008-06-10 10:56:53 +0800 (星期二, 10 六月 2008) $
</pre>


<!-- Other pages seem to use "h2" for ToC, but I think "h1" works
     better, because the ToC is fundamentally different from other
     sections and therefore it's confusing when it looks the same as
     the others. -->
<div class="h1"><!-- no 'id' or 'title' attribute for ToC -->
<h1>内容列表</h1>

<ul>
<li><a href="#participating">参与社区</a></li>
<li><a href="#docs">原理和文档</a></li>
<li><a href="#code-to-read">如何读代码</a></li>
<li><a href="#directory-layout">目录布局</a></li>
<li><a href="#coding-style">代码风格</a></li>
<li><a href="#secure-coding">安全的编码准则</a></li>
<li><a href="#destruction-of-stacked-resources">堆栈资源的销毁</a></li>
<li><a href="#documenting">程序注释</a></li>
<li><a href="#use-page-breaks">使用分页符</a></li>
<li><a href="#error-messages">错误信息约定</a></li>
<li><a href="#other-conventions">其他约定</a></li>
<li><a href="#apr-pools">APR池用法约定</a></li>
<li><a href="#apr-status-codes">APR返回状态码</a></li>
<li><a href="#exception-handling">异常处理</a></li>
<li><a href="#automated-tests">自动测试</a></li>
<li><a href="#write-test-cases-first">在编码前写测试用例</a></li>
<li><a href="#server-debugging">调试服务器</a></li>
<li><a href="#tracing-memory-leaks">跟踪内存泄露</a></li>
<li><a href="#log-messages">编写日志信息</a></li>
<li><a href="#crediting">记录贡献者信息</a></li>
<li><a href="#patches">补丁提交指导方针</a></li>
<li><a href="#filing-issues">填写bugs / issues</a></li>
<li><a href="#issue-triage">Issue分级</a></li>
<li><a href="#commit-access">提交权限</a></li>
<li><a href="#lightweight-branches">使用轻量级分支</a></li>
<li><a href="#configury">在unix下配置和构建代码</a></li>
<li><a href="#releasing">如何释出一个分发包</a></li>
<li><a href="#release-numbering">发布版本编号，及兼容性和反对点Release numbering, compatibility, and deprecation</a></li>
<li><a href="#release-stabilization">稳定性处理何管理发布包</a></li>
<li><a href="#tarball-signing">签署源代码发布的软件包</a></li>
<li><a href="#l10n">本地化 (l10n)</a></li>
</ul>

</div>


<div class="h2" id="participating" title="participating">
<h2>参与社区</h2>

<p>尽管Subversion本来由CollabNet 
(<a href="http://www.collab.net">http://www.collab.net</a>)发起和主办, 
但它是一个符合BSD许可协议的完全开源项目.  大量的开发者为CollabNet工作，
一些开发者为其他大公司工作（例如RedHat），更多的其他优秀志愿者仅仅是为
了建立一个更好的版本控制系统感兴趣而参加这个项目。
</p>

<p>社区主要通过邮件列表和Subversion仓库来交流
repository.  </p>
<p>参与请访问网站<a href="http://subversion.tigris.org/"
>http://subversion.tigris.org/</a>并且 </p>

<ul>
<li><p>加入 "dev", "svn", 和 "announce"邮件列表 . 开发邮件列表, 
dev@subversion.tigris.org, 几乎所有的讨论都发生在这里，所有的开发问题
都应当在这里，虽然你可能需要首先检查列表归档，”svn”邮件列表接受自动提
交emails 
</p></li>

<li><p>可以在
       <a href="http://svn.collab.net/repos/svn/trunk/"
               >http://svn.collab.net/repos/svn/trunk/</a>得到最新的源码.
       <br />
       新的开发始终在主干上发生.  Bug修改，增强，和新特性都是从主干上移
       植到各种发布分支上.</p></li> 
</ul>

<p>有多种方法可以加入这个工程，例如写代码，测试，或者帮助管理bug数据库。
如果你想参与贡献，可以浏览以下两个网址:</p>

<ul>
<li><p>bugs/issues数据库
       <a href="http://subversion.tigris.org/issue-tracker.html"
               >http://subversion.tigris.org/issue-tracker.html</a></p></li>

<li><p>大小合适的任务
       <a href="http://subversion.tigris.org/project_tasks.html"
               >http://subversion.tigris.org/project_tasks.html</a></p></li>
</ul>

<p>T提交代码，仅仅发送你的补丁包到
dev@subversion.tigris.org.  不，等等，首先要读完这个文件的其他部分
，然后再开始发送补丁到
dev@subversion.tigris.org. :-)</p>

<p>为了帮助管理问题数据库, 需要重读问题概要，寻找和测试问题是否有效或者同
其他问题重复。这两种现象非常普遍，首先因为bug经常会在其他代码修改中不
知不觉的修改了，其次人们有时会提交一个问题，而没有注意到这个问题已经
被报告了。如果你不确定这个问题，在邮件列表  dev@subversion.tigris.org 中提交问题.
("Subversion: We're here to help you help us!")</p>

<p>另一种帮助是建立自动构建和测试套件，并在一种平台上运行，然后把输
出发给svn-breakage@subversion.tigris.org邮件列表，可以在网址
<a href="http://subversion.tigris.org/servlets/ProjectMailingListList"
        >http://subversion.tigris.org/servlets/ProjectMailingListList</a>
看到关于 svn-breakage邮件列表更详细的信息.</p>

</div>


<div class="h2" id="docs" title="docs">
<h2>原理和文档</h2>

<ol>
<li><p>设计</p>

     <p>A <a href="design.html">设计规格说明书</a> w在2000年6月写成，现在有点过时. 
     但它仍给出了仓库的内部工作原理的非常好的理论介绍，还有Sbuversion不同的层。
</p>
</li>

<li><p>API文档</p>
     <p>参见<a href="#doxygen-docs">public API
     documentation</a>来获取更多的信息.</p>
</li>

<li><p>Delta Editors</p>
     <p>Karl Fogel wrote a chapter for O'Reilly's 2007 book
     <a href="http://beautifulcode.oreillynet.com/">
     Beautiful Code: Leading Programmers Explain How They Think</a>
     covering the design and use of
     <a href="http://www.red-bean.com/kfogel/beautiful-code/bc-chapter-02.html">
     Subversion's delta editor interface</a>.</p>
</li>

<li> <p>网络协议</p>

     <p>The <a href="webdav-usage.html">WebDAV 用法</a> 文档介绍了Subversion DAV网络协议，
     这是对HTTP的非标准的扩展，以"http://" 或者 “https://”开头来使用URL。</p>

     <p>The <a
href="http://svn.collab.net/repos/svn/trunk/subversion/libsvn_ra_svn/protocol"
     >SVN协议</a> 文档包含ra_svn网络协议正式的描述，这个自定的协议使用端口号3690（
     认），URL由”svn://”或者”svn+ssh://”开始。</p>
</li>

<li><p>用户手册</p>

     <p>《使用Subversion进行版本控制》 是由O’Reilly出版的，讲解如何高效使用Subversion
     的一本书。这本书的内容是自由的，它被积极的修订，在线版本
     at <a href="http://svnbook.red-bean.com"
     >http://svnbook.red-bean.com</a>.可以使用。XML资源和翻译成其他语言 在他们自己的仓库中
     <a href="http://svn.red-bean.com/svnbook"
     >http://svn.red-bean.com/svnbook</a>中维护。</p>
</li>

<li><p>系统注释</p>

     <p>大量设计思路，尤其是系统方面的文档被记录在独立的目录
     <a href="http://svn.collab.net/repos/svn/trunk/notes/">notes</a>
     里.</p>
</li>

</ol>

</div>


<div class="h2" id="code-to-read" title="code-to-read">
<h2>如何读代码</h2>

<p>在你贡献代码之前，你需要熟悉现有的代码和接口.</p>

<p>签出subversion的代码 (如果你还没有提交权限的帐户, 你
可以使用匿名用户) — 这样你就可以看代码了.</p>

<p>在目录'subversion/include/'里 是大量的带有大量文档注释的头文件。  
如果你仔细阅读这些，你将对实现细节有一个很好的理解，这里是建议阅读顺序:</p>

<ol>
<li><p>最基本的功能块: svn_string.h, svn_error.h, svn_types.h</p>
</li>
<li><p>有用的公用程序:  svn_io.h, svn_path.h, svn_hash.h, svn_xml.h</p>
</li>
<li><p>关键的接口:  svn_delta.h</p>
</li>
<li><p>客户端的接口:  svn_ra.h, svn_wc.h, svn_client.h</p>
</li>
<li><p>仓库和版本的文件系统:  svn_repos.h, svn_fs.h</p>
</li>
</ol>

<p>Subversion只使用ANSI/ISO C和Apache运行时库(ARP)来尽量保持简单，
 APR是Apache的httd服务器使用的轻便的一层工具库，可以在
  <a href="http://apr.apache.org/"
>http://apr.apache.org/</a>看到详细的信息.</p>

<p>由于Subversion非常依赖APR, 所以如果没有阅某些APR头文件，就难于
理解Subversion (头文件在'apr/include/'):
</p>

<ul>
<li><p>内存池:  apr_pools.h</p></li>
<li><p>文件系统存取  apr_file_io.h</p></li>
<li><p>散列和数组:  apr_hash.h, apr_tables.h</p></li>
</ul>

<p>Subversion也尽力交付一个可靠的和安全的软件。 这是可以理解c语言安
全编程的开发人员才可以获得. 请参考'notes/assurance.txt' 获得这后面的
全部依据。 具体来说，你应当仔细读取《 David Wheeler's Secure Programming》
 ('notes/assurance.txt').如果你对安全编程的任意一点有疑问，我们鼓励你在开
 发者的邮件列表中探讨.</p>

</div>


<div class="h2" id="directory-layout" title="directory-layout">
<h2>目录结构</h2>

<p>源代码树的简要指南:</p>

<ul>
<li><p><tt>doc/</tt><br />
    用户和开发者文档.</p>
</li>
<li><p><tt>www/</tt><br />
    Subversion的web页面 (最新内容在
      <a href="http://subversion.tigris.org/"
      >http://subversion.tigris.org/</a>).</p>
</li>
<li><p><tt>tools/</tt><br />
    同Subversion一起的资料，但Subversion不依赖于这些，在tools/ 目录的代码
    也被Subversion工程维护着，和Subversion自己有相同的开源版权.</p>
</li>
<li><p><tt>contrib/</tt><br />
    同Subversion一起的工具，但Subversion不依赖于这些, 这些被独立维护，而不参与Subversion的开发。
    在contrib/目录下的代码是开源的，但可能与Subversion有不同的许可协议或版权保留.</p>
</li>
<li><p><tt>packages/</tt><br />
    帮助打包系统的资料, 例如 rpm 和 dpkg.</p>
</li>
<li><p><tt>subversion/</tt><br />
    Subversion自己的源代码 (相对与其他外部库来说).</p>
</li>
<li><p><tt>subversion/include/</tt><br />
    使用Subversion库的公共头文件.</p>
</li>
<li><p><tt>subversion/include/private/</tt><br />
    Subversion库内部共享的私有头文件.</p>
</li>
<li><p><tt>subversion/libsvn_fs/</tt><br />
    "filesystem" API的版本.</p>
</li>
<li><p><tt>subversion/libsvn_repos/</tt><br />
    构建存储功能所需要的`libsvn_fs'核心.</p>
</li>
<li><p><tt>subversion/libsvn_delta/</tt><br />
   代码树的累加备份，注释的累加备份，属性的累加备份代码.</p>
</li>
<li><p><tt>subversion/libsvn_wc/</tt><br />
    工作拷贝的共用代码.</p>
</li>
<li><p><tt>subversion/libsvn_ra/</tt><br />
    仓库存取的公用代码.</p>
</li>
<li><p><tt>subversion/libsvn_client/</tt><br />
    客户端操作的公用代码.</p>
</li>
<li><p><tt>subversion/svn/</tt><br />
    客户端的命令行程序.</p>
</li>
<li><p><tt>subversion/tests/</tt><br />
    自动测试套件.</p>
</li>
<li><p><tt>apr/</tt><br />
    Apache便携式运行时库.  (注意: 这些不在Subversion的代码库中，
    如果你还没有这些代码，阅读INSTALL来获取如何得到这些代码.)</p>
</li>
<li><p><tt>neon/</tt><br />
    从Joe Orton这里得到Neon库.  (注意: 这些代码不在Subversion的代码库中，
    如果你还没有这些代码，阅读INSTALL来获取如何得到这些代码.)</p>
</li>
</ul>

</div>


<div class="h2" id="coding-style" title="coding-style">
<h2>代码风格</h2>

<p>Subversion使用ANSI C, 遵从GNU编码标准,
除了我们我们不在函数名和参数列表的左括号之间没有空格，Emacs的用户可以只
加载svn-dev.el ，就得到正确的添加空格行为
 (如果'enable-local-eval'被设置，许多源文件将自动的采用这个行为).</p>

<p>阅读<a href="http://www.gnu.org/prep/standards.html"
>http://www.gnu.org/prep/standards.html</a>，这是GNU编码标准的全部描述. 
 下面例子演示了很多重要的格式指导，包括我们的在参数列表和括号之间没有空格:</p>

<pre>
   char *                                     /* func type on own line */
   argblarg(char *arg1, int arg2)             /* func name on own line */
   {                                          /* first brace on own line */
     if ((some_very_long_condition &amp;&amp; arg2)   /* indent 2 cols */
         || remaining_condition)              /* new line before operator */
       {                                      /* brace on own line, indent 2 */
         arg1 = some_func(arg1, arg2);        /* NO SPACE BEFORE PAREN */
       }                                      /* close brace on own line */
     else
       {
         do                                   /* format do-while like this */
           {
             arg1 = another_func(arg1);
           }
         while (*arg1);
       }
   }
</pre>

<p>
通常，当你确定了操作符的优先级后，在使用括号方面要大方。
而且需要增加空格和新行来避免"code crunch" 
不要太担心代码的垂直密度；
代码的可读性比代码在屏幕上少占一行更重要.</p>

</div>


<div class="h2" id="secure-coding" title="secure-coding">
<h2>安全编码指导方针</h2>

<p>几乎像其他任何编程语言，C语言有一些不好的特性，这可以使攻击者以预定的方式
使你程序运行失败，来使攻击者受益。
这些指导方针的目标是使你知道C中的陷阱，从而可以应用到Subversion工程。
当你审查代码时，我们鼓励你记住这些陷阱，因为即使是最熟练和细心的程序员也可能偶尔犯错误。</p>

<p>输入确认是接受合法的输入，拒绝任何其他输入，
代码在所有不可信赖的输入上必须执行输入确认。
</p>

<p>Security boundaries:</p>

<p>A security boundary in the Subversion server code must be
identified as such as this enables auditors to quickly determine the
quality of the boundary.  Security boundaries exist where the running
code has access to information the user does not or where the code
runs with privileges above those of the user making the
request. Typical examples of such is code that does access control or
an application with the SUID bit set.</p>

<p>Functions which make calls to a security boundary must include
validation checks of the arguments passed. Functions which themselves
are security boundaries should audit the input received and alarm when
invoked with improper values. </p>

<p>[### todo: need some examples from Subversion here...]</p>

<p>字符串操作:</p>

<p>使用apr_strings.h提供的字符串函数而不要使用标准C库的函数来写字符串，
APR函数由于做了边界检查和目标地址的自动分配，所以是安全的。
虽然在某些情况下使用C字符串函数是安全的(例如当你已经知道了字符串源和
目的地长度),但不论什么情况还是使用APR函数，这样代码更友好，更容易检查。
</p>

<p>密码存储:</p>

<p>帮助用户保密密码：当客户端读写密码时，应当保证文件的读写权限是0600,
如果这个文件可以被其他用户读取，客户端应当退出，并且告诉用户由于泄露的
危险而需要改变文件的模式.</p>

</div>


<div class="h2" id="destruction-of-stacked-resources"
     title="destruction-of-stacked-resources">
<h2>栈资源的释放</h2>

<p>为了确保程序的正确功能，一些资源需要释放。
这些资源包括文件，尤其是在Windows下打开的不能被删除的文件。</p>

<p>当写一个创建并返回一个流的API时，在这个流的背后是文件或者其他类型的流。
为了确保构建这个流上的资源被正确的释放，必须正确的调用构建这个流的资源的销毁函数。
</p>

<p>第一个在 <a href="http://svn.haxx.se/dev/archive-2005-12/0487.shtml">
http://svn.haxx.se/dev/archive-2005-12/0487.shtml</a>
最后的在<a href="http://svn.haxx.se/dev/archive-2005-12/0633.shtml">
http://svn.haxx.se/dev/archive-2005-12/0633.shtml</a> 
这些讨论一个对于文件，流，编辑器，和窗口句柄等来说，更通用的条款。
</p>

<p>As Greg Hudson put it:</p>

<blockquote>
<p>On consideration, here is where I would like us to be:</p>

<ul><li>Streams which read from or write to an underlying object own that
object, i.e. closing the stream closes the underlying object, if
applicable.</li>

<li>The layer (function or data type) which created a stream is
responsible for closing it, except when the above rule applies.</li>

<li>Window handlers are thought of as an odd kind of stream, and passing
the final NULL window is considered closing the stream.</li>
</ul>

<p>If you think of apply_textdelta as creating a window handler, then I
don't think we're too far off.  svn_stream_from_aprfile isn't owning its
subsidiary file, svn_txdelta_apply is erroneously taking responsibility
for closing the window stream it is passed, and there may be some other
deviations.</p>
</blockquote>

<p>There is one exception to the rules above though.  When a stream is passed
to a function as an argument (for example: the 'out' parameter of
svn_client_cat2()), that routine can't call the streams destructor, since
it did not create that resource.</p>

<p>If svn_client_cat2() creates a stream, it must also call the destructor
for that stream.  By the above model, that stream will call the destructor
for the 'out' parameter.  This is however wrong, because the responsibility
to destruct the 'out' parameter lies elsewhere.</p>

<p>To solve this problem, at least in the stream case, svn_stream_disown()
has been introduced.  This function wraps a stream, making sure it's
<em>not</em> destroyed, even though any streams stacked upon it may try
to do so.</p>

</div>


<div class="h2" id="documenting" title="documenting">
<h2>文档</h2>

<div class="h3" id="document-everything" title="document-everything">
<h3>文档化任何资料</h3>
<p>任何函数，无论是公有的还是内部的，都必须以文档开始，来描述这个函数是做什么的
文档应当提到被函数接收的每一个参数，每一个可能的返回值，以及在什么情况下返回一个错误.</p>

<p>内部文档参数应当大些开头，即使他们在实际声明中没有大写，因为这样可以方便人们的可读性
</p>

<p>对于公有的或者半公有的API函数，文档应当在声明他的.h文件中，
否则就应当在定义他的.c文件中。</p>

<p>对于结构类型，应当像记录结构本身一样记录每一个独立的成员的文档.</p>

<p>对于实际的代码，每一个函数的内部文档是相当的长，所以一些熟悉Subversion的人可以理解正在实施中的算法。
不要包含明显的或过长的文档，注释应当帮助理解代码，而不是妨碍理解。</p>

<p>例如:</p>
<pre>
  <span style="color: red;">/*** How not to document.  Don't do this. ***/</span>

  /* Make a foo object. */
  static foo_t *
  make_foo_object(arg1, arg2, apr_pool_t *pool)
  {
     /* Create a subpool. */
     apr_pool_t *subpool = svn_pool_create(pool);

     /* Allocate a foo object from the main pool */
     foo_t *foo = apr_palloc(pool, sizeof(*foo));
     ...
  }
</pre>

<p>不要像上面那样，文档应当和代码大小适合，应该像下面这样:</p>
<pre>
      /* Transmit the segment (if its within the scope of our concern). */
      SVN_ERR(maybe_crop_and_send_segment(segment, start_rev, end_rev,
                                          receiver, receiver_baton, subpool));

      /* If we've set CURRENT_REV to SVN_INVALID_REVNUM, we're done
         (and didn't ever reach END_REV).  */
      if (! SVN_IS_VALID_REVNUM(current_rev))
        break;

      /* If there's a gap in the history, we need to report as much
         (if the gap is within the scope of our concern). */
      if (segment-&gt;range_start - current_rev &lt; 1)
        {
          svn_location_segment_t *gap_segment;
          gap_segment = apr_pcalloc(subpool, sizeof(*gap_segment));
          gap_segment-&gt;range_end = segment-&gt;range_start - 1;
          gap_segment-&gt;range_start = current_rev + 1;
          gap_segment-&gt;path = NULL;
          SVN_ERR(maybe_crop_and_send_segment(gap_segment, start_rev, end_rev,
                                              receiver, receiver_baton,
                                              subpool));
        }
</pre>

<p>再次阅读Subversion代码，在实践中得到如何写很好的注释，
尤其要看 
<a href="http://svn.collab.net/repos/svn/trunk/subversion/include/">
subversion/include/*.h</a> 头文件的例子.
</p>

</div>

<div class="h3" id="doxygen-docs" title="doxygen-docs">
<h3>公开的API文档Public API Documentation</h3>
<p>我们使用 <a href="http://www.doxygen.org/">Doxygen</a> 格式来提供接口文档。
这意味着任何东西都在公共的头文件中。  公有API文档的<a href="http://svn.collab.net/svn-doxygen/">快照
</a>每天晚上从最新的源码中产生。</p>

<p>我们仅仅使用了
<a href="http://www.stack.nl/~dimitri/doxygen/commands.html">doxygen
命令</a>的一小部分来标注我们的源码. 当我们写doxygen文档时，有下面的一些约定要遵守:</p>
<ul>
  <li>Use complete sentences and prose descriptions of the function, 参数名前使用<code>@a</code>, 类型
  和宏名称前使用<code>@c</code>.</li>

  <li>使用 <code>&lt;tt&gt;...&lt;/tt&gt;</code>来显示多个单词，使用
  <code>@p</code>来显示仅仅一个单词为打印机字体。</li>

  <li>常量值, 例如 <code>TRUE</code>, <code>FALSE</code> 和
  <code>NULL</code> 应当全部字母大写.</li>

  <li>当几个函数功能相关时，定义一个组名，使用<code>@defgroup</code> and <code>@{...@}</code>来分组.</li>
</ul>

<p>全部命令列表参见<a href="http://www.stack.nl/~dimitri/doxygen/manual.html">Doxygen
手册</a> .</p>

</div>

</div>


<div class="h2" id="use-page-breaks" title="use-page-breaks">
<h2>Using page breaks</h2>

<p>We're using page breaks (the Ctrl-L character, ASCII 12) for
section boundaries in both code and plaintext prose files.  Each
section starts with a page break, and immediately after the page break
comes the title of the section.</p>

<p>This helps out people who use the Emacs page commands, such as
`pages-directory' and `narrow-to-page'.  Such people are not as scarce
as you might think, and if you'd like to become one of them, then add
(require 'page-ext) to your .emacs and type C-x C-p C-h sometime.</p>

</div>


<div class="h2" id="error-messages" title="error-messages">
<h2>错误信息约定</h2>

<p>对于错误信息有以下信息约定:</p>

<ul>

<li><p>提供的错误信息必须加在 
     subversion/include/svn_error_codes.h文件中.</p></li>

<li><p>信息以大写字母开头.</p></li>

<li><p>尽量保持少于70个字符.</p></li>

<li><p>不要以句号(".")来终止错误信息.</p></li>

<li><p>错误信息不要包含新行字符.</p></li>

<li><p>引用的资料是用单引号(例如 "'some info'").</p></li>

<li><p>不要包含发生错误信息函数的名称，如果Subversion在编译时
使用'--enable-maintainer-mode'配置选项，他可以自己提供这个信息.</p></li>

<li><p>当错误字符串包含目录或文件名时，一定要包含他们
 (例如 "Can't find '/path/to/repos/userfile'").</p></li>

<li><p>当错误信息字符串包含路径或文件名时，在包含之前一定要使用 <a
     href="http://svn.collab.net/svn-doxygen/svn__path_8h.html#a1"
     ><tt>svn_path_local_style()</tt></a> 函数来转换 (since
     paths passed to and from Subversion APIs are assumed to be
     in <a href="http://svn.collab.net/svn-doxygen/svn__path_8h.html#a0"
     >canonical form</a>).</p></li>

<li><p>不要使用Subversion约定的缩写(例如使用"repository"
     而不是"repo", "working copy"而不是"wc").</p></li>

<li><p>如果你想给错误信息加一个解释，可以加在冒号后面,例如:</p>
     <pre>
       "Invalid " SVN_PROP_EXTERNALS " property on '%s': "
       "target involves '.' or '..'".
     </pre></li>

<li><p>建议或者其他需要增加的可以在分号后面,例如:</p>
     <pre>
       "Can't write to '%s': object of same name already exists; remove "
       "before retrying".
     </pre></li>

<li><p>尽力保证遵守这些约定，所以请避免使用其他的分隔符例如'--'或其他来分开信息不同部分。
</p></li>

</ul>

<p>Also read about <a href="#l10n">Localization</a>.</p>

</div>


<div class="h2" id="other-conventions" title="other-conventions">
<h2>Other conventions</h2>

<p>In addition to the GNU standards, Subversion uses these
conventions:</p>

<ul>
<li><p>When using a path or file name as input to most <a
     href="http://svn.collab.net/svn-doxygen/">Subversion APIs</a>, be
     sure to convert them to Subversion's internal/canonical form
     using the <a href="http://svn.collab.net/svn-doxygen/svn__path_8h.html#a0"
     ><tt>svn_path_internal_style()</tt></a> API.  Alternately, when
     receiving a path or file name as output from a Subversion API,
     convert them into the expected form for your platform using the 
     <a href="http://svn.collab.net/svn-doxygen/svn__path_8h.html#a1"
     ><tt>svn_path_local_style()</tt></a> API.</p></li>

<li><p>Use only spaces for indenting code, never tabs.  Tab display
      width is not standardized enough, and anyway it's easier to
      manually adjust indentation that uses spaces.</p>
</li>

<li><p>Restrict lines to 79 columns, so that code will display well in a
      minimal standard display window.  (There can be exceptions, such
      as when declaring a block of 80-column text with a few extra
      columns taken up by indentation, quotes, etc., if splitting each
      line in two would be unreasonably messy.)</p>
</li>

<li><p>All published functions, variables, and structures must be signified
      with the corresponding library name - such as libsvn_wc's
      svn_wc_adm_open.  All library-internal declarations made in a
      library-private header file (such as libsvn_wc/wc.h) must be signified
      by two underscores after the library prefix (such as
      svn_wc__ensure_directory).  All declarations private to a single file
      (such as the static function get_entry_url inside of
      <tt>libsvn_wc/update_editor.c</tt>) do not require any
      additional namespace decorations.  Symbols that need to be used
      outside a library, but still are not public are put in a shared
      header file in the <tt>include/private/</tt> directory, and use
      the double underscore notation.  Such symbols may be used by
      Subversion core code only.</p>
      
      <p>To recap:</p>
      <pre>
         /* Part of published API: subversion/include/svn_wc.h */
         svn_wc_adm_open()            
         #define SVN_WC_ADM_DIR_NAME ...
         typedef enum svn_wc_schedule_t ...

         /* For use within one library only: subversion/libsvn_wc/wc.h */
         svn_wc__ensure_directory()   
         #define SVN_WC__BASE_EXT ... 
         typedef struct svn_wc__compat_notify_baton_t ...

         /* For use within one file: subversion/libsvn_wc/update_editor.c */ 
         get_entry_url()
         struct handler_baton {

         /* For internal use in svn core code only:
            subversion/include/private/svn_wc_private.h */
         svn_wc__entry_versioned()
      </pre>

     <p>Pre-Subversion 1.5, private symbols which needed to be used
       outside of a library were put into public header files,
       using the double underscore notation.  This practice has been
       abandoned, and any such symbols are legacy, maintained for <a
       href="#release-numbering">backwards compatibility</a>.</p>
</li>

<li><p>In text strings that might be printed out (or otherwise made
      available) to users, use only forward quotes around paths and
      other quotable things.  For example:</p>
      <pre>
         $ svn revert foo
         svn: warning: svn_wc_is_wc_root: 'foo' is not a versioned resource
         $
      </pre>

      <p>There used to be a lot of strings that used a backtick for
      the first quote (`foo' instead of 'foo'), but that looked bad in
      some fonts, and also messed up some people's auto-highlighting,
      so we settled on the convention of always using forward
      quotes.</p>
</li>

<li><p>If you use Emacs, put something like this in your .emacs file,
      so you get svn-dev.el and svnbook.el when needed:</p>
      <pre>
         ;;; Begin Subversion development section
         (defun my-find-file-hook ()
           (let ((svn-tree-path (expand-file-name "~/projects/subversion"))
                 (book-tree-path (expand-file-name "~/projects/svnbook")))
             (cond
              ((string-match svn-tree-path buffer-file-name)
               (load (concat svn-tree-path "/tools/dev/svn-dev")))
              ((string-match book-tree-path buffer-file-name)
               ;; Handle load exception for svnbook.el, because it tries to
               ;; load psgml, and not everyone has that available.
               (condition-case nil
                   (load (concat book-tree-path "/src/tools/svnbook"))
                 (error
                  (message "(Ignored problem loading svnbook.el.)")))))))

         (add-hook 'find-file-hooks 'my-find-file-hook)
         ;;; End Subversion development section
      </pre>

      <p>You'll need to customize the path for your setup, of course.
      You can also make the regexp to string-match more selective; for
      example, one developer says:</p>
      <pre>
      &gt; Here's the regexp I'm using:
      &gt; 
      &gt;     "src/svn/[^/]*/\\(subversion\\|tools\\|build\\)/"
      &gt;
      &gt; Two things to notice there: (1) I sometimes have several
      &gt; working copies checked out under ...src/svn, and I want the
      &gt; regexp to match all of them; (2) I want the hook to catch only
      &gt; in "our" directories within the working copy, so I match
      &gt; "subversion", "tools" and "build" explicitly; I don't want to
      &gt; use GNU style in the APR that's checked out into my repo. :-)
      </pre>
</li>

<li><p>We have a tradition of not marking files with the names of
      individual authors (i.e., we don't put lines like
      "Author:&nbsp;foo" or "@author&nbsp;foo" in a special position
      at the top of a source file).  This is to discourage
      territoriality&nbsp;&mdash;&nbsp;even when a file has only one
      author, we want to make sure others feel free to make changes.
      People might be unnecessarily hesitant if someone appears to
      have staked a personal claim to the file.</p>
</li>

<li><p>Put two spaces between the end of one sentence and the start of
      the next.  This helps readability, and allows people to use
      their editors' sentence-motion and -manipulation commands.</p>
</li>

<li><p>There are many other unspoken conventions maintained throughout
      the code, that are only noticed when someone unintentionally
      fails to follow them.  Just try to have a sensitive eye for the
      way things are done, and when in doubt, ask.</p>
</li>
</ul>

</div>


<div class="h2" id="apr-pools" title="apr-pools">
<h2>APR pool usage conventions</h2>

<p>(This assumes you already basically understand how APR pools work;
see apr_pools.h for details.)</p>

<p>Applications using the Subversion libraries must call
apr_initialize() before calling any Subversion functions.</p>

<p>Subversion's general pool usage strategy can be summed up in two
principles:</p>

<ol>
<li><p>The call level that created a pool is the only place to clear or
       destroy that pool.</p>
</li>
<li><p>When iterating an unbounded number of times, create a subpool
       before entering the iteration, use it inside the loop and clear 
       it at the start of each iteration, then destroy it after the loop 
       is done, like so:</p>
       <pre>
         apr_pool_t *subpool = svn_pool_create(pool);

         for (i = 0; i &lt; n; ++i)
         {
           svn_pool_clear(subpool);
           do_operation(..., subpool);
         }

         svn_pool_destroy(subpool);
       </pre>
</li>
</ol>

<p>By using a loop subpool for loop-bounded data, you ensure O(1) instead
of O(N) memory leakage should the function return abruptly from
within the loop (say, due to error).  That's why you shouldn't make a
subpool for data which persists throughout a function, but instead
should use the pool passed in by the caller.  That memory will be
reclaimed when the caller's pool is cleared or destroyed.  If the
caller is invoking the callee in a loop, then trust the caller to take
care of clearing the pool on each iteration.  The same logic
propagates all the way up the call stack.</p>

<p>The pool you use also helps readers of the code understand object
lifetimes.  Is a given object used only during one iteration of the
loop, or will it need to last beyond the end of the loop?  For
example, pool choices indicate a lot about what's going on in this
code:</p>

<pre>
      apr_hash_t *persistent_objects = apr_hash_make(pool);
      apr_pool_t *subpool = svn_pool_create(pool);

      for (i = 0; i &lt; n; ++i)
      {
        const char *intermediate_result;
        const char *key, *val;
        
        svn_pool_clear(subpool);
        SVN_ERR(do_something(&amp;intermediate_result, ..., subpool));
        SVN_ERR(get_result(intermediate_result, &amp;key, &amp;val, ..., pool));
        apr_hash_set(persistent_objects, key, APR_HASH_KEY_STRING, val);
      }
      svn_pool_destroy(subpool);

      return persistent_objects;
</pre>

<p>Except for some legacy code, which was written before these
principles were fully understood, virtually all pool usage in
Subversion follows the above guidelines.</p>

<p>One such legacy pattern is a tendency to allocate an object inside
a pool, store the pool in the object, and then free that pool (either
directly or through a close_foo() function) to destroy the object.</p>

<p>For example:</p>

<pre>
   <span style="color: red;">/*** Example of how NOT to use pools.  Don't be like this. ***/</span>

   static foo_t *
   make_foo_object(arg1, arg2, apr_pool_t *pool)
   {
      apr_pool_t *subpool = svn_pool_create(pool);
      foo_t *foo = apr_palloc(subpool, sizeof(*foo));

      foo-&gt;field1 = arg1;
      foo-&gt;field2 = arg2;
      foo-&gt;pool   = subpool;
   }

   [...]

   [Now some function calls make_foo_object() and returns, passing
   back a new foo object.]

   [...]

   [Now someone, at some random call level, decides that the foo's
   lifetime is over, and calls svn_pool_destroy(foo-&gt;pool).]
</pre>

<p>This is tempting, but it defeats the point of using pools, which is
to not worry so much about individual allocations, but rather about
overall performance and lifetime groups.  Instead, foo_t generally
should not have a `pool' field.  Just allocate as many foo objects as
you need in the current pool&nbsp;&mdash;&nbsp;when that pool gets
cleared or destroyed, they will all go away simultaneously.</p>

<p>See also the <a href="#exception-handling">Exception handling</a>
section, for details of how resources associated with a pool are
cleaned up when that pool is destroyed.</p>

<p>In summary:</p>

<ul>

<li><p>Objects should not have their own pools.  An object is
       allocated into a pool defined by the constructor's caller.  The
       caller knows the lifetime of the object and will manage it via
       the pool.</p>
</li>

<li><p>Functions should not create/destroy pools for their operation;
       they should use a pool provided by the caller.  Again, the
       caller knows more about how the function will be used, how
       often, how many times, etc. thus, it should be in charge of the
       function's memory usage.</p>

    <p>For example, the caller might know that the app will exit upon
       the function's return. Thus, the function would create extra
       work if it built/destroyed a pool. Instead, it should use the
       passed-in pool, which the caller is going to be tossing as part
       of app-exit anyway.</p>
</li>

<li><p>Whenever an unbounded iteration occurs, an iteration subpool
       should be used.</p>
</li>

<li><p>Given all of the above, it is pretty well mandatory to pass a
       pool to every function.  Since objects are not recording pools
       for themselves, and the caller is always supposed to be
       managing memory, then each function needs a pool, rather than
       relying on some hidden magic pool.  In limited cases, objects
       may record the pool used for their construction so that they
       can construct sub-parts, but these cases should be examined
       carefully.</p>
</li>
</ul>


<p>See also <a href="#tracing-memory-leaks">Tracking down memory
leaks</a> for tips on diagnosing pool usage problems.</p>

</div>


<div class="h2" id="apr-status-codes" title="apr-status-codes">
<h2>APR status codes</h2>

<p>Always check for APR status codes (except APR_SUCCESS) with the
APR_STATUS_IS_...() macros, not by direct comparison. This is required
for portability to non-Unix platforms.</p>

</div>


<div class="h2" id="exception-handling" title="exception-handling">
<h2>Exception handling</h2>

<p>OK, here's how to use exceptions in Subversion.</p>

<ol>

<li><p>Exceptions are stored in svn_error_t structures:</p>

<pre>
typedef struct svn_error_t
{
  apr_status_t apr_err;      /* APR error value, possibly SVN_ custom err */
  const char *message;       /* details from producer of error */
  struct svn_error_t *child; /* ptr to the error we "wrap" */
  apr_pool_t *pool;          /* place to generate message strings from */
  const char *file;          /* Only used iff SVN_DEBUG */
  long line;                 /* Only used iff SVN_DEBUG */
} svn_error_t;
</pre>

</li>

<li><p>If you are the <em>original</em> creator of an error, you would do
       something like this:</p>

    <pre>
return svn_error_create(SVN_ERR_FOO, NULL, 
                        "User not permitted to write file");
    </pre>

    <p>NOTICE the NULL field... indicating that this error has no
    child, i.e. it is the bottom-most error.</p>

    <p>See also the <a href="#error-messages"> section on writing
    error messages</a>.</p>

    <p>Subversion internally uses UTF-8 to store its data. This also
    applies to the 'message' string. APR is assumed to return its data
    in the current locale, so any text returned by APR needs
    conversion to UTF-8 before inclusion in the message string.</p>
</li>

<li><p>If you <em>receive</em> an error, you have three choices:</p>

    <ol>
    <li><p>Handle the error yourself.  Use either your own code, or
           just call the primitive svn_handle_error(err).  (This
           routine unwinds the error stack and prints out messages
           converting them from UTF-8 to the current locale.)</p>

        <p>When your routine receives an error which it intends to
           ignore or handle itself, be sure to clean it up using
           svn_error_clear(). Any time such an error is not cleared
           constitutes a <em>memory leak</em>.</p>
    </li>

    <li><p>Throw the error upwards, unmodified:</p>

        <pre>
        error = some_routine(foo);
        if (error)
          return (error);
        </pre>

        <p>Actually, a better way to do this would be with the
        SVN_ERR() macro, which does the same thing:</p>
        <pre>
        SVN_ERR(some_routine(foo));
        </pre>
     </li>

    <li><p>Throw the error upwards, wrapping it in a new error
           structure by including it as the "child" argument:</p>

        <pre>
        error = some_routine(foo);
        if (error)
          {
           svn_error_t *wrapper = svn_error_create(SVN_ERR_FOO, error,
                                                   "Authorization failed");
           return wrapper;
          }
        </pre>

        <p>Of course, there's a convenience routine which creates a
           wrapper error with the same fields as the child, except for
           your custom message:</p>

        <pre>
        error = some_routine(foo);
        if (error)
          {
           return svn_error_quick_wrap(error, 
                                       "Authorization failed");
          }
        </pre>

        <p>The same can (and should) be done by using the SVN_ERR_W()
           macro:</p>

        <pre>
          SVN_ERR_W(some_routine(foo), "Authorization failed");
        </pre>
    </li>
    </ol>

    <p>In cases (b) and (c) it is important to know that resources
    allocated by your routine which are associated with a pool, are
    automatically cleaned up when the pool is destroyed. This means
    that there is no need to cleanup these resources before passing
    the error. There is therefore no reason not to use the SVN_ERR()
    and SVN_ERR_W() macros.  Resources associated with pools are:</p>

    <ul>

    <li><p>Memory</p></li>

    <li><p>Files</p>

        <p>All files opened with apr_file_open are closed at pool
        cleanup.  Subversion uses this function in its svn_io_file_*
        api, which means that files opened with svn_io_file_* or
        apr_file_open will be closed at pool cleanup.</p>

        <p>Some files (lock files for example) need to be removed when
        an operation is finished. APR has the APR_DELONCLOSE flag for
        this purpose.  The following functions create files which are
        removed on pool cleanup:</p>

        <ul>
        <li><p>apr_file_open and svn_io_file_open (when passed the 
               APR_DELONCLOSE flag)</p></li>
        <li><p>svn_io_open_unique_file (when passed TRUE in its
               delete_on_close)</p></li>
        </ul>
   
        <p>Locked files are unlocked if they were locked using
        svn_io_file_lock.</p>
    </li>
    </ul>

</li>
</ol>

</div>


<div class="h2" id="automated-tests" title="automated-tests">
<h2>Automated tests</h2>

<p>For a description of how to use and add tests to Subversion's
automated test framework, please read <a
href="http://svn.collab.net/repos/svn/trunk/subversion/tests/README"
>subversion/tests/README</a> and <a
href="http://svn.collab.net/repos/svn/trunk/subversion/tests/cmdline/README"
>subversion/tests/cmdline/README</a>.</p>

<p>Various people have arranged for the automated test framework to
run at regular intervals on their own machines, sending the results to
the svn-breakage@subversion.tigris.org mailing list.  The more
different platforms the tests run on, the more quickly we can detect
portability bugs in Subversion.  If you'd like to send svn-breakage
messages too, use the <a
href="http://svn.collab.net/repos/svn/trunk/tools/test-scripts/svntest/" 
>svntest</a> framework (start at the <a
href="http://svn.collab.net/repos/svn/trunk/tools/test-scripts/svntest/README" 
>README</a>).</p>

<p>Lieven Govaerts has set up a
<a href="http://buildbot.sourceforge.net/" >BuildBot</a> build/test
farm at <a href="http://www.mobsol.be/buildbot/"
>http://www.mobsol.be/buildbot/</a>, see his message</p>

<pre>
   <a href="http://subversion.tigris.org/servlets/ReadMsg?list=dev&amp;msgNo=114212">http://subversion.tigris.org/servlets/ReadMsg?list=dev&amp;msgNo=114212</a>
   (Thread URL: <a href="http://subversion.tigris.org/servlets/BrowseList?list=dev&amp;by=thread&amp;from=450110">http://subversion.tigris.org/servlets/BrowseList?list=dev&amp;by=thread&amp;from=450110</a>)
   Message-ID: 20060326205918.F3C50708B0@adicia.telenet-ops.be
   From: "Lieven Govaerts" &lt;lgo@mobsol.be&gt;
   To: &lt;dev@subversion.tigris.org&gt;
   Subject: Update: Subversion build and test farm with Buildbot.
   Date: Sun, 26 Mar 2006 22:56:11 +0200
</pre>

<p>for more details.  (<a href="http://buildbot.sourceforge.net/"
>BuildBot</a> is a system for centrally managing multiple automated
testing environments; it's especially useful for portability testing,
including of uncommitted changes.)</p>

</div>


<div class="h2" id="write-test-cases-first" title="write-test-cases-first">
<h2>在编码前写测试用例</h2>

<pre>
From: Karl Fogel &lt;kfogel@collab.net&gt;
Subject: writing test cases
To: dev@subversion.tigris.org
Date: Mon, 5 Mar 2001 15:58:46 -0600

我们当中很多实现过文件系统接口的人都已经养成一个习惯了，就是在编写真正代码之前先写好
测试用例了（参考fs-test.c）。这确实帮了我们很多忙 —— 一个好处就是，它强制我们提前准确地
定义好要完成的任务，同时它也让我们在第一次编码尝试的时候更快的发现Bug（还有第二个好处，第三个。。。）。

我想把这个实践推荐给大家。如果你正在实现一个接口，或者增加一个完全新的功能，或者只是在
修正一个Bug，测试是个好主意。而且如果你本来就打算写测试的话，那么你还不如先把测试写了再说。:-)

Yoshiki Hayashi最近在发送他写的补丁的时候一直都附带着测试用例，这也是我为什么要写这封邮件
鼓励大家去做同样的事的缘故。有了这些测试用例，那么补丁就可以更容易的检查，因为这些测试把补丁
的目的表现地非常清楚。这就像拥有另外一份日志一样，运行一下我们就知道其正确性了。

需要多说一句，我们对这方面并不做任何硬性的规定，至少暂时还不需要。如果你在代码中发现一个Bug，
但是你只够时间来编写补丁而没有时间去写测试用例，那也是没问题的 —— 补丁本身就已经很有用了，会有其他人帮你写测试用例的。

随着Subversion变得越来越复杂，自动化的测试套件变得越来越重要了，所以我们还是尽快的养成这一个
习惯吧。

-K
</pre>

</div>


<div class="h2" id="server-debugging" title="server-debugging">
<h2>服务器调试</h2>

<div class="h3" id="debugging-ra-dav" title="debugging-ra-dav">
<h3>DAV服务器的调试</h3>

<p>‘mod_dav_svn.so’库包含了Subversion服务器的主要逻辑；它在mod_dav内部以模块的形式运行，而mod_dav本身是以模块的形式在httpd进程里运行。因为httpd很可能使用的是共享的动态链接库，你一般情况下是不能在启动Apache的时候，在GDB等调试器中预先设置好断点的。所以你只能先启动，然后中断httpd，然后再设置断点，再继续运行：</p>

<pre>
   % gdb httpd
   (gdb) run -X
   ^C
   (gdb) break some_func_in_mod_dav_svn
   (gdb) continue
</pre>

<p>-X参数和-DONE_PROCESS、-DNO_DETACH作用相同，都是保证httpd是以单线程的形式运行的，并且一直挂到tty上面。一旦它启动了，它就会一直等待请求；这个时候你就可以按下Control-C然后设置你的断点了。</p>

<p>你很可能需要查看Apache的运行日志。</p>

<pre>
   /usr/local/apache2/logs/error_log
   /usr/local/apache2/logs/access_log
</pre>

<p>这会帮助你找到可能出错的地方和下断点的位置。</p>

</div>

<div class="h3" id="debugging-ra-svn" title="debugging-ra-svn">
<h3>在Unix上面调试ra_svn客户端和服务端</h3>

<p>ra_svn上面的Bug通常出现的时候总是伴随着下面两个比较费解的错误提示信息：</p>

<pre>
  svn: Malformed network data
  svn: Connection closed unexpectedly
</pre>

<p>（第一条信息又意味着数据流在隧道模式下被用户的点文件（译者注：Dotfiles，指Unix中以“.”号开头的文件）或者钩子脚本破坏了；参考<a href="http://subversion.tigris.org/issues/show_bug.cgi?id=1145"
>问题编号&nbsp;#1145</a>。）第一条信息通常告诉你你得在客户端进行调试；第二条信息通常告诉你你得在服务器端进行调试。</p>

<p>编译时使用--disable-shared --enable-maintainer-mode选项调试ra_svn的时候更简单些。使用后面这个选项，当出现错误信息时，调试器会提示你应该在哪一行设置断点；不然的话你得找到marshal.c文件末尾vparse_tuple()所在的行数，这个函数会返回“Malformed network data”的错误信息。</p>

<p>要调试客户端，只需要简单的在gdb调试器中激活这个模块，设置断点，然后运行那些出错的命令：</p>

<pre>
  % gdb svn
  (gdb) break marshal.c:NNN
  (gdb) run ARGS
  Breakpoint 1, vparse_tuple (list=___, pool=___, fmt=___, 
    ap=___) at subversion/libsvn_ra_svn/marshal.c:NNN
  NNN                                 "Malformed network data");
</pre>

<p>下面讲一些比较有用的东西：</p>

<ul>
<li><p>查看一下堆栈信息你可以看到在哪个协议上出现了问题。</p>
</li>

<li><p>使用“print *conn”命令可以显示连接的缓冲情况。read_buf，read_ptr还有read_end表示读取缓冲区，你可以在上面看到编排器（Marshaller）正在查找的数据。（因为read_buf在read_end处通常不是以0结束符结尾的，所以要注意，不要理所当然的以为缓冲区里还有一些垃圾数据。)</p>
</li>

<li><p>格式化字符串决定了编排器期望查找的数据。</p>
</li>
</ul>

<p>要在守护进程模式下调试服务端，先在gdb调试器中启动它，设置断点（通常客户端的“Connection closed unexpectedly”错误说明服务端发生了“Malformed network data”错误，不过有时候是因为内存转储的缘故），然后使用“-X”参数以单连接形式提供服务：</p>

<pre>
  % gdb svnserve
  (gdb) break marshal.c:NNN
  (gdb) run -X
</pre>

<p>然后运行那些出问题的客户端命令。这样看的话，其实跟调试客户端是很像的。</p>

<p>在隧道模式下调试服务端会比较痛苦。你需要在svnserve里面靠近main()方法的上面加入类似“{ int x = 1; while (x); }”这样的语句，然后把新的svnserve放在服务器上的用户路径中。然后开始操作，gdb会把服务器上的进程附加进来，执行“set x = 0”，然后进入你想查看的代码。</p>

</div>

</div>


<div class="h2" id="tracing-memory-leaks" title="tracing-memory-leaks">
<h2>跟踪内存泄露</h2>

<p>我们使用的APR池在严格模式下基本不可能会出现内存泄露的问题。所有申请的内存最终都会被释放掉。但是有时候某个操作可能会占用过多的不必要的内存；例如，对比较大的源码树的检出不应该比相对比较小的源码树的检出占用的内存多出那么多。如果是这样的话，那么通常意味着我们从池里申请的内存生命周期过长了。</p>

<p>如果你有比较喜欢的内存泄露检测工具，你可以配置其使用--enable-pool-debug选项（这会导致每一次申请池的时候都使用自带的malloc()函数），使它在操作中途退出，然后查看检测结果。如果没用的话，那么使用下面的办法：</p>

<ul>

<li><p>配置参数--enable-pool-debug=verbose-alloc。确保所有APR和Subversion都经过重新编译，这样每一次的内存分配都会有“文件和行数”（file-and-line）的信息。</p>
</li>

<li><p>执行操作，把stderr标准错误输出设备指向一个文件。希望你硬盘空间足够大。</p>
</li>

<li><p>打开文件，你会看到很多类似下面这样的行：</p>

   <pre>
    POOL DEBUG: [5383/1024] PCALLOC (      2763/      2763/      5419) \
    0x08102D48 "subversion/svn/main.c:612"                             \
    &lt;subversion/libsvn_subr/auth.c:122&gt; (118/118/0)
   </pre>

    <p>你所最关心的应该在第十个字段上（用引号括起来的），这个字段告诉你池分配时的文件和行号。定位到该文件指定的行，然后判断池的生命周期。在上面的例子中，main.c:612说明这次分配是在svn客户端的根级别的池中分配的。如果这样的分配在一次操作过程中重复出现多次，那么意味着有可能这里出现了内存泄露。第十一个字段（括号括起来的）指出了分配的文件和行号本身。</p>
</li>

</ul>

</div>


<div class="h2" id="log-messages" title="log-messages">
<h2>编写日志信息</h2>

<p>每一次提交都需要填写日志信息。</p>

<p>查看日志信息的人可能是一个已经熟悉Subversion的开发人员，但他不一定对每一次提交的修改都很熟悉。通常当一个人回过头来查看版本库中的某次修改的时候，他已经完全忘记了当初做这次修改了的原因了，即使当时这次修改是他自己提交的！当时的所有讨论、相关的邮件列表内容还有其他信息可能都已经不记得了。唯一关于这次修改的提示性信息只能从提交的日志信息中去查看了，或者使用diff比较一下。而且，大家经常会很频繁的重新去查看当时做的修改：例如，提交之后过了几个月了，这些修改已经被放到维护分支里面了。</p>

<p>日志信息是对修改内容的一个介绍。可以以一行先简单概述修改的摘要信息，然后如果需要的话，再细述修改的内容的细节。以摘要方式写日志这种方式不仅可以让开发人员更好的看懂剩余的日志信息，而且对于像“CIA”这种类似IRC即时通讯软件的可以自动将提交信息中的首行发布到实时论坛上面的机器人程序也是有利的。（细节请参考：<a href="http://cia.navi.cx/">http://cia.navi.cx/</a>。）不过，如果当次提交只是对一个文件做了简单的修改的话，那么你可以省掉开头的摘要信息，直接描述细节的内容，格式就像你下面看到的这种“文件名-符号”的形式。</p>

<p>在日志信息中，要使用完整的语句去描述清楚，而不要过分讲究言简意赅，那样容易让人产生歧义，况且把你想要表达的意思写清楚了也不过需要多花你几秒钟的时间而已。不过有些表述像“文档修订”，“新文件”虽然用词比较少，但是也是可以接受的，因为大家都已经非常熟悉这些用词的意思了，更细节的内容就应该放到源代码里面了。</p>

<p>日志信息应该把受影响的函数，变量，宏，makefile目标，语法规则等的名称都写清楚，这还包括那些在此次提交中被删除的符号。这能够帮助其他人以后更好的检索日志内容。不要使用通配符来代替这些名称，因为这些名称可能以后有人检索的时候会用到。例如下面这样的日志格式就不是很好：</p>

<pre>
   * subversion/libsvn_ra_pigeons/twirl.c
     (twirling_baton_*): Removed these obsolete structures.
     (handle_parser_warning): Pass data directly to callees, instead
      of storing in twirling_baton_*.

   * subversion/libsvn_ra_pigeons/twirl.h: Fix indentation.
</pre>

<p>以后，当有人想要查看‘twirling_baton_fast’文件的历史操作的时候，如果他们使用“_fast”去搜索的话，那么他们可能就找不到相应的内容了。一个比较好的日志格式应该是：</p>

<pre>
   * subversion/libsvn_ra_pigeons/twirl.c
     (twirling_baton_fast, twirling_baton_slow): Removed these
      obsolete structures. 
     (handle_parser_warning): Pass data directly to callees, instead
      of storing in twirling_baton_*. 

   * subversion/libsvn_ra_pigeons/twirl.h: Fix indentation.
</pre>

<p>只有当上面两个结构（译者注：twirling_baton_fast，twirling_baton_slow）的完整名称在日志的其他地方被引用了的时候，我们才可以在描述‘handle_parser_warning’的时候才可以使用通配符。</p>

<p>注意到每个文件都以“*”号开头放到单独一个条目中，文件内的修改以符号作为分组依次列举，符号使用括号包起来然后后面跟着一个冒号，最后是描述修改内容的文字。请遵循这样的格式，即使只修改了一个文件&nbsp;&mdash;&nbsp;这样不仅仅保证了一致性和可读性，还允许软件自动为日志信息的每个条目加上不同的颜色。</p>

<p>不过也有例外情况，就是当你对多个文件做同样内容的修改的时候，要把所有修改的文件放置到一个条目中去。例如下面这样：</p>

<pre>
   * subversion/libsvn_ra_pigeons/twirl.c,
     subversion/libsvn_ra_pigeons/roost.c:
     Include svn_private_config.h.
</pre>

<p>如果所有改动的文件都位于源代码树上比较深的层次的时候，你可以在条目之前注明公共的文件路径前缀，这样文件名就不用使用完整路径了，例如：</p>

<pre>
   [in subversion/bindings/swig/birdsong]

   * dialects/nightingale.c (get_base_pitch): Allow 3/4-tone
     pitch variation to account for trait variability amongst
     isolated populations Erithacus megarhynchos.

   * dialects/gallus_domesticus.c: Remove. Unreliable due to
     extremely low brain-to-body mass ratio.
</pre>

<p>如果你的更改是针对issue tracker工具中的特定的issue的时候，那么在日志中要包含类似“issue #N”这样的字符串，但还是要写清楚更改的内容。例如，如果一个补丁解决了issue #1729，那么日志信息可能像下面这样：</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.
</pre>

<p>尽量把相关修改内容放在一起。例如，如果你创建了svn_ra_get_ansible2()，然后把svn_ra_get_ansible()废弃了，那么关于这两个修改的描述信息在日志信息中应该放到一块：</p>

<pre>
   * subversion/include/svn_ra.h
     (svn_ra_get_ansible2): New prototype, obsoletes svn_ra_get_ansible.
     (svn_ra_get_ansible): Deprecate.
</pre>

<p>对于比较大的修改或者修改集，使用空行对这些修改集分段落进行记录。每一段应该描述完成同一目标的修改集的内容，并且在段落之前加上一两句概述的信息。当然，对于非常独立的修改，最好是独立提交。</p>

<p>关于提交别人编写的补丁或者别人建议的更改的时候如何注明他们的贡献的问题，参考 <a href="#crediting">贡献</a>。 </p>

<p>一个人不应该需要通过日志条目去了解当前的代码。如果你发现自己在日志中对更改的内容做了很详尽的解释的话，那么你应该考虑清楚，你所写的内容是不是应该和需要解释的代码一起放到代码注释里头。下面这个例子才是正确的做法：</p>

<pre>
   (consume_count): If `count' is unreasonable, return 0 and don't
    advance input pointer.
</pre>

<p>然后在‘cplus-dem.c’文件中的‘consume_count’处：</p>

<pre>
   while (isdigit((unsigned char)**type))
     {
       count *= 10;
       count += **type - '0';
       /* A sanity check.  Otherwise a symbol like
         `_Utf390_1__1_9223372036854775807__9223372036854775'
         can cause this function to return a negative value.
         In this case we just consume until the end of the string.  */
      if (count &gt; strlen(*type))
        {
          *type = save;
          return 0;
        }
</pre>

<p>这就是为什么当我们添加了一个新函数的时候，我们只需要在日志中注明“新函数”——因为所有的细节内容都应该放在源代码中。</p>

<p>我们并不需要每次更改都把受影响的所有内容都注明清楚，有时候你需要凭常识去排除一些例外的情况。例如，当你做了一些更改，但是并不会对其他程序产生什么影响（例如，重命名一个变量），那么你不需要把所有受影响的函数都列举出来，你只需要注明“所有调用者都受影响”（All Caller Changed）即可。当重命名一个符号的时候，记住要注明旧名称和新名称，这样便于跟踪；示例参考r20946。</p>

<p>通常来说，写出便于检索的日志条目，和浪费时间或者写一大堆详尽的可读性非常差的条目之间就像拉力赛一样。你需要使用上面提出的指导意见，还有你自己做出最好的判断，并且多考虑一下你的同事。（还有，<a
href="http://subversion.tigris.org/servlets/BrowseList?listName=svn">
运行"svn log"</a>看看其他人都是怎么写日志的。）</p>

<p>对文档或者翻译的日志信息格式要求比较松一些。如果要求对每个符号都命名的话那也太不现实了，甚至如果只是在一个连续的过程里面对例如翻译做了一些增量的改动的话，那么甚至都不需要把每个文件的名称都写到日志里头。只需要简单的概述一下更改内容，例如：“More work on Malagasy translation.”请用英语来写日志，这样所有参与项目的成员才都能看懂日志，了解你做的更改。</p>

</div>


<div class="h2" id="crediting" title="crediting">
<h2>Crediting</h2>

<p>It is very important to record code contributions in a consistent
and parseable way.  This allows us to write scripts to figure out who
has been actively contributing&nbsp;&mdash;&nbsp;and what they have
contributed&nbsp;&mdash;&nbsp;so we can <a
href="http://www.red-bean.com/svnproject/contribulyzer/">spot
potential new committers quickly</a>.  The Subversion project uses
human-readable but machine-parseable fields in log messages to
accomplish this.</p>

<p>When committing a patch written by someone else, use
"Patch&nbsp;by:&nbsp;" at the beginning of a line to indicate the
author:</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.

   Patch by: J. Random &lt;jrandom@example.com&gt;
</pre>

<p>If multiple individuals wrote the patch, list them each on a
separate line&nbsp;&mdash;&nbsp;making sure to start each continuation
line with whitespace.  Non-committers should be listed by name, if
known, and e-mail.  Full and partial committers should be listed by
their canonical usernames from <a
href="http://svn.collab.net/repos/svn/trunk/COMMITTERS"
>COMMITTERS</a> (the leftmost column in that file).  Additionally,
"me" is an acceptable shorthand for the person actually committing the
change.</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.

   Patch by: J. Random &lt;jrandom@example.com&gt;
             Enrico Caruso &lt;codingtenor@codingtenor.com&gt;
             jcommitter
             me
</pre>

<p>If someone found the bug or pointed out the problem, but didn't
write the patch, indicate their contribution with
"Found&nbsp;by:&nbsp;":</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.

   Found by: J. Random &lt;jrandom@example.com&gt;
</pre>

<p>If someone suggested something useful, but didn't write the patch,
indicate their contribution with "Suggested&nbsp;by:&nbsp;":</p>

<pre>
   Extend the Contribulyzer syntax to distinguish finds from ideas.

   * www/hacking.html (crediting): Adjust accordingly.

   Suggested by: dlr
</pre>

<p>If someone reviewed the change, use "Review&nbsp;by:&nbsp;"
(or "Reviewed&nbsp;by:&nbsp;" if you prefer):</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.

   Review by: Eagle Eyes &lt;eeyes@example.com&gt;
</pre>

<p>A field may have multiple lines, and a log message may contain any
combination of fields:</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.

   Patch by: J. Random &lt;jrandom@example.com&gt;
             Enrico Caruso &lt;codingtenor@codingtenor.com&gt;
             me
   Found by: J. Random &lt;jrandom@example.com&gt;
   Review by: Eagle Eyes &lt;eeyes@example.com&gt;
              jcommitter
</pre>

<p>Further details about a contribution should be listed in a
parenthetical aside immediately after the corresponding field.  Such
an aside always applies to the field right above it; in the following
example, the fields have been spaced out for readability, but note
that the spacing is optional and not necessary for parseability:</p>

<pre>
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.

   Patch by: J. Random &lt;jrandom@example.com&gt;
   (Tweaked by me.)

   Review by: Eagle Eyes &lt;eeyes@example.com&gt;
              jcommitter
   (Eagle Eyes caught an off-by-one-error in the basename extraction.)
</pre>

<p>Currently, these fields</p>

<pre>
   Patch by:
   Suggested by:
   Found by:
   Review by:
</pre>

<p>are the only officially-supported crediting fields (where
"supported" means scripts know to look for them), and they are widely
used in Subversion log messages.  Future fields will probably be of
the form "VERB&nbsp;by:&nbsp;", and from time to time someone may use
a field that sounds official but really is not&nbsp;&mdash;&nbsp;for
example, there are a few instances of "Reported&nbsp;by:&nbsp;".
These are okay, but try to use an official field, or a parenthetical
aside, in preference to creating your own.  Also, don't use
"Reported&nbsp;by:&nbsp;" when the reporter is already recorded in an
issue; instead, simply refer to the issue.</p>

<p>Look over Subversion's existing log messages to see how to use
these fields in practice.  This command from the top of your trunk
working copy will help:</p>

<pre>
svn log | contrib/client-side/search-svnlog.pl "(Patch|Review|Suggested) by: "
</pre>

<p><b>Note:</b> The "Approved&nbsp;by:&nbsp;" field seen in some
commit messages is totally unrelated to these crediting fields, and is
generally not parsed by scripts.  It is simply the standard syntax for
indicating either who approved a partial committer's commit outside
their usual area, or (in the case of merges to release branches) who
voted for the change to be merged.</p>

</div>


<div class="h2" id="patches" title="patches">
<h2>Patch submission guidelines</h2>

<p>Mail patches to dev@subversion.tigris.org, starting the subject
line with <tt>[PATCH]</tt>.  This helps our patch manager spot patches
right away.  For example:</p>

<pre>
   Subject: [PATCH] fix for rev printing bug in svn status
</pre>

<p>If the patch addresses a particular issue, include the issue number
as well: "<tt>[PATCH]&nbsp;issue&nbsp;#1729: ...</tt>".  Developers
who are interested in that particular issue will know to read the
mail.</p>

<p>A patch submission should contain one logical change; please don't
mix N unrelated changes in one submission&nbsp;&mdash;&nbsp;send N
separate emails instead.</p>

<p>Generate the patch using <tt>svn&nbsp;diff</tt> from the top of a
Subversion trunk working copy.  If the file you're diffing is not
under revision control, you can achieve the same effect by using
<tt>diff&nbsp;-u</tt>.</p>

<p>Please include a log message with your patch.  A good log message
helps potential reviewers understand the changes in your patch, and
increases the likelihood that it will be applied.  You can put the log
message in the body of the email, or at the top of the patch
attachment (see below).  Either way, it should follow the guidelines
given in <a href="#log-messages">Writing log messages</a>, and be
enclosed in triple square brackets, like so:</p>

<pre>
   [[[
   Fix issue #1729: Don't crash because of a missing file.

   * subversion/libsvn_ra_ansible/get_editor.c
     (frobnicate_file): Check that file exists before frobnicating.
   ]]]
</pre>

<p>(The brackets are not actually part of the log message, they're
just a way to clearly mark off the log message from its surrounding
context.)</p>

<p>If possible, send the patch as an attachment with a mime-type of
text/x-diff, text/x-patch, or text/plain.  Most people's mailreaders
can display those inline, and having the patch as an attachment allows
them to extract the patch from the message conveniently.  Never send
patches in archived or compressed form (e.g., tar, gzip, zip, bzip2),
because that prevents people from reviewing the patch directly in
their mailreaders.</p>

<p>If you can't attach the patch with one of these mime-types, or if
the patch is very short, then it's okay to include it directly in the
body of your message.  But watch out: some mail editors munge inline
patches by inserting unasked-for line breaks in the middle of long
lines.  If you think your mail software might do this, then please use
an attachment instead.</p>

<p>If the patch implements a new feature, make sure to describe the
feature completely in your mail; if the patch fixes a bug, describe
the bug in detail and give a reproduction recipe.  An exception to
these guidelines is when the patch addresses a specific issue in the
issues database&nbsp;&mdash;&nbsp;in that case, just refer to the
issue number in your log message, as described
in <a href="#log-messages">Writing log messages</a>.</p>

<p>It is normal for patches to undergo several rounds of feedback and
change before being applied.  Don't be discouraged if your patch is
not accepted immediately&nbsp;&mdash;&nbsp;it doesn't mean you goofed,
it just means that there are a <em>lot</em> of eyes looking at every
code submission, and it's a rare patch that doesn't have at least a
little room for improvement.  After reading people's responses to your
patch, make the appropriate changes and resubmit, wait for the next
round of feedback, and lather, rinse, repeat, until some committer
applies it.</p>

<p>If you don't get a response for a while, and don't see the patch
applied, it may just mean that people are really busy.  Go ahead and
repost, and don't hesitate to point out that you're still waiting for
a response.  One way to think of it is that patch management is highly
parallizable, and we need you to shoulder your share of the management
as well as the coding.  Every patch needs someone to shepherd it
through the process, and the person best qualified to do that is the
original submitter.</p>

<div class="h3" id="patch-manager" title="patch-manager">
<h3>The "Patch Manager" Role</h3>

<p>Subversion usually has a Patch Manager, whose job is to watch the
dev@ mailing list and make sure that no patches "slip through the
cracks".</p>

<p>This means watching every thread containing "[PATCH]" mails, and
taking appropriate action based on the progress of the thread.  If the
thread resolves on its own (because the patch gets committed, or
because there is consensus that the patch doesn't need to be applied,
or whatever) then no further action need be taken.  But if the thread
fades out without any clear decision, then the patch needs to be saved
in the issue tracker.  This means that a summary of any discussion
threads around that patch, and links to relevant mailing list
archives, will be added to some issue in the tracker.  For a patch
which addresses an existing issue tracker item, the patch is saved to
that item.  Otherwise, a new issue of type 'PATCH' is filed, and the
patch is saved to that new issue.</p>

<p>The Patch Manager needs a basic technical understanding of
Subversion, and the ability to skim a thread and get a rough
understanding of whether consensus has been reached, and if so, of
what kind.  It does <em>not</em> require actual Subversion development
experience or commit access.  Expertise in using one's mail reading
software is optional, but recommended :-).</p>

<p>The current patch manager is Daniel Shahaf 
&lt;danielsh@tigris.org&gt;.</p>

</div>

</div>


<div class="h2" id="filing-issues" title="filing-issues">
<h2>Filing bugs / issues</h2>

<p>This pretty much says it all:</p>

<pre>
   From: Karl Fogel &lt;kfogel@collab.net&gt;
   Subject: Please ask on the list before filing a new issue.
   To: dev@subversion.tigris.org
   Date: Tue, 30 Jul 2002 10:51:24 (CDT)
   
   Folks, we're getting tons of new issues, which is a Good Thing in
   general, but some of them don't really belong in the issue tracker.
   They're things that would be better solved by a quick conversation
   here on the dev list.  Compilation problems, behavior questions,
   feature ideas that have been discussed before, that sort of thing.
   
   *Please* be more conservative about filing issues.  The issues
   database is physically much more cumbersome than email.  It wastes
   people's time to have conversations in the issues database that should
   be had in email.  (This is not a libel against the issue tracker, it's
   just a result of the fact that the issues database is for permanent
   storage and flow annotation, not for real-time conversation.)
   
   If you encounter a situation where Subversion is clearly behaving
   wrongly, or behaving opposite to what the documentation says, then
   it's okay to file the issue right away (after searching to make sure
   it isn't already filed, of course!).  But if you're
   
      a) Requesting a new feature, or
      b) Having build problems, or
      c) Not sure what the behavior should be, or
      d) Disagreeing with current intended behavior, or
      e) Not TOTALLY sure that others would agree this is a bug, or
      f) For any reason at all not sure this should be filed,
   
   ...then please post to the dev list first.  You'll get a faster
   response, and others won't be forced to use the issues database to
   have the initial real-time conversations.
   
   Nothing is lost this way.  If we eventually conclude that it should be
   in the issue tracker, then we can still file it later, after the
   description and reproduction recipe have been honed on the dev list.
   
   Thank you,
   -Karl
</pre>
   
</div>

<div class="h2" id="issue-triage" title="issue-triage">
<h2>Issue triage</h2>

<p>When an issue is filed, it goes into the special milestone "---",
meaning <em>unmilestoned</em>.  This is a holding area that issues
live in until someone gets a chance to look at them and decide what to
do.</p>

<p>The unmilestoned issues are listed first when you sort by
milestone, and <em>issue triage</em> is the process of trawling
through all the <a
href="http://subversion.tigris.org/issues/buglist.cgi?component=subversion&amp;issue_status=UNCONFIRMED&amp;issue_status=NEW&amp;issue_status=STARTED&amp;issue_status=REOPENED&amp;order=issues.target_milestone%2C%20issues.priority%2C%20issues.issue_type"
>open issues</a> (starting with the unmilestoned ones), determining
which are important enough to be fixed now, which can wait until
another release, which are duplicates of existing issues, which have
been resolved already, etc.  For each issue that will remain open, it
also means making sure that the various fields are set appropriately:
type, subcomponent, platform, OS, version, keywords (if any), and so
on.</p>

<p>Here's an overview of the process (in this example, 1.5 is the next
release, so urgent issues would go there):</p>

<pre>
    for i in issues_marked_as("---"):
      if issue_is_a_dup_of_some_other_issue(i):
        close_as_dup(i)
      elif issue_is_invalid(i):
        # A frequent reason for invalidity is that the reporter
        # did not follow the <a href="http://subversion.tigris.org/issue-tracker.html#buddy-system">"buddy system"</a> for filing.
        close_as_invalid(i)
      elif issue_already_fixed(i):
        close_as_fixed(i)
      elif issue_unreproducible(i):
        close_as_worksforme(i)
      elif issue_is_real_but_we_won't_fix_it(i):
        close_as_wontfix(i)
      elif issue_is_closeable_for_some_other_reason(i):
        close_as_it_for_that_reason(i)

      # Else issue should remain open, so DTRT with it...

      # Set priority, platform, subcomponent, etc, as needed.
      adjust_all_fields_that_need_adjustment(i)

      # Figure out where to put it.
      if issue_is_a_lovely_fantasy(i):
        move_to_milestone(i, "blue-sky")
      if issue_is_not_important_enough_to_block_any_particular_release(i):
        move_to_milestone(i, "nonblocking")
      elif issue_resolution_would_require_incompatible_changes(i):
        move_to_milestone(i, "2.0")
      elif issue_hurts_people_somewhat(i):
        move_to_milestone(i, "1.6")  # or whatever
      elif issue_hurts_people_a_lot(i):
        move_to_milestone(i, "1.5-consider")
      elif issue_hurts_and_hurts_and_won't_stop_hurting(i):
        move_to_milestone(i, "1.5")
</pre>

</div>

<div class="h2" id="commit-access" title="commit-access">
<h2>Commit access</h2>

<p>There are two types of commit access: full and partial.  Full means
anywhere in the tree, partial means only in that committer's specific
area(s) of expertise.  The <a
href="http://svn.collab.net/repos/svn/trunk/COMMITTERS"
>COMMITTERS</a> file lists all committers, both full and partial, and
says the domains for each partial committer.</p>

<div class="h3" id="full-commit-access" title="full-commit-access">
<h3>How full commit access is granted</h3>

<p>After someone has successfully contributed a few non-trivial
patches, some full committer, usually whoever has reviewed and applied
the most patches from that contributor, proposes them for commit
access.  This proposal is sent only to the other full committers --
the ensuing discussion is private, so that everyone can feel
comfortable speaking their minds.  Assuming there are no objections,
the contributor is granted commit access.  The decision is made by
consensus; there are no formal rules governing the procedure, though
generally if someone strongly objects the access is not offered, or is
offered on a provisional basis.</p>

<p><i>The primary criterion for full commit access is good
judgment.</i></p>

<p>You do not have to be a technical wizard, or demonstrate deep
knowledge of the entire codebase, to become a full committer.  You
just need to know what you don't know.  If your patches adhere to the
guidelines in this file, adhere to all the usual unquantifiable rules
of coding (code should be readable, robust, maintainable, etc.), and
respect the Hippocratic Principle of "first, do no harm", then you
will probably get commit access pretty quickly.  The size, complexity,
and quantity of your patches do not matter as much as the degree of
care you show in avoiding bugs and minimizing unnecessary impact on
the rest of the code.  Many full committers are people who have not
made major code contributions, but rather lots of small, clean fixes,
each of which was an unambiguous improvement to the code.  (Of course,
this does not mean the project needs a bunch of very trivial patches
whose only purpose is to gain commit access; knowing what's worth a
patch post and what's not is part of showing good judgement :-) .)</p>

<p>To assist developers in discovering new committers, we record
patches and other contributions in a <a href="#crediting">special
crediting format</a>, which is then parsed to produce a
browser-friendly <a
href="http://www.red-bean.com/svnproject/contribulyzer/">contribution
list</a>, updated nightly.  If you're thinking of proposing someone
for commit access and want to look over all their changes, that <a
href="http://www.red-bean.com/svnproject/contribulyzer/">contribution
list</a> might be the most convenient place to do it.</p>

</div>

<div class="h3" id="partial-commit-access" title="partial-commit-access">
<h3>How partial commit access is granted</h3>

<p>A full committer sponsors the partial committer.  Usually this
means the full committer has applied several patches to the same area
from the proposed partial committer, and realizes things would be
easier if the person were just committing directly.  Approval is not
required from the full committers; it is assumed that sponsors know
what they're doing and will watch the partial committer's first few
commits to make sure everything's going smoothly.</p>

<p>Patches submitted by a partial committer may be committed by that
committer even if they are outside that person's domain.  This
requires approval (often expressed as a +1 vote) from at least one
full committer.  In such a case, the approval should be noted in the
log message, like so:</p>

<pre>
   Approved by: lundblad
</pre>

<p>Any full committer may offer anyone commit access to an
experimental branch at any time.  It is not necessary that the
experimental branch have a high likelihood of being merged to trunk
(although that's always a good goal to aim for).  It's just as
important that the full committer&nbsp;&mdash;&nbsp;all the full
committers, actually&nbsp;&mdash;&nbsp;view such branches as training
grounds for new developers, by giving feedback on the commits.  The
goal of these branches is both to get new code into Subversion and to
get new developers into the project.  See also the <a
href="#lightweight-branches" >section on lightweight branches</a>, and
this mail:</p>

<pre>
   <a href="http://subversion.tigris.org/servlets/ReadMsg?list=dev&amp;msgNo=132746"
>http://subversion.tigris.org/servlets/ReadMsg?list=dev&amp;msgNo=132746
   From: Karl Fogel &lt;kfogel@red-bean.com&gt;
   To: dev@subversion.tigris.org
   Subject: branch liberalization (was: Elego tree conflicts work)
   Date: Tue, 20 Nov 2007 10:49:38 -0800
   Message-Id: &lt;87y7cswy4d.fsf@red-bean.com&gt;</a>
</pre>

</div>

<div class="h3" id="contrib-area" title="contrib-area">
<h3>The contrib/ area</h3>

<p>When a tool is accepted into the <i>contrib/</i> area, we
automatically offer its author partial commit access to maintain the
tool there.  Any full committer can sponsor this.  Usually no
discussion or vote is necessary, though if there are objections then
the usual decision-making procedures apply (attempt to reach consensus
first, then vote among the full committers if consensus cannot be
reached).</p>

<p>Code under contrib/ must be open source, but need not have the same
license or copyright holder as Subversion itself.</p>

</div>

<div class="h3" id="obvious-fix" title="obvious-fix">
<h3>The "obvious fix" rule</h3>

<p>Any committer, whether full or partial, may commit fixes for
obvious typos, grammar mistakes, and formatting problems wherever they
may be&nbsp;&mdash;&nbsp;in the web pages, API documentation, code
comments, commit messages, etc.  We rely on the committer's judgement
to determine what is "obvious"; if you're not sure, just ask.</p>

<p>Whenever you invoke the "obvious fix" rule, please say so in
the <a href="#log-messages">log message</a> of your commit. For example:</p>

<pre>
   Obvious fix.

   * www/hacking.html: Kill some typos.
</pre>

</div>

</div>

<div class="h2" id="lightweight-branches" title="lightweight-branches">
<h2>Use lightweight branches</h2>

<p>If you're working on a feature or bugfix in stages involving
multiple commits, and some of the intermediate stages aren't stable
enough to go on trunk, then create a temporary branch in /branches.
There's no need to ask&nbsp;&mdash;&nbsp;just do it.  It's fine to try
out experimental ideas in a temporary branch, too.  And all the
preceding applies to partial as well as full committers.</p>

<p>If you're just using the branch to "checkpoint" your code, and
don't feel it's ready for review, please put some sort of notice at
the top of the log message, such as:</p>

<pre>
   *** checkpoint commit -- please don't waste your time reviewing it ***
</pre>

<p>And if a later commit on that branch <em>should</em> be reviewed,
then please supply, in the log message, the appropriate 'svn diff'
command, since the diff would likely involve two non-adjacent commits
on that branch, and reviewers shouldn't have to spend time figuring
out which ones they are.</p>

<p>When you're done with the branch&nbsp;&mdash;&nbsp;when you've
either merged it to trunk or given up on it&nbsp;&mdash;&nbsp;please
remember to remove it.</p>

<p>See also the <a href="#partial-commit-access" >section on partial
commit access</a> for our policy on offering commit access to
experimental branches.</p>

</div>

<div class="h2" id="configury" title="configury">
<h2>The configuration/build system under unix</h2>

<p>Greg Stein wrote a custom build system for Subversion, which had
been using `automake' and recursive Makefiles.  Now it uses a single,
top-level Makefile, generated from Makefile.in (which is kept under
revision control).  `Makefile.in' in turn includes `build-outputs.mk',
which is automatically generated from `build.conf' by the
`gen-make.py' script.  Thus, the latter two are under revision
control, but `build-outputs.mk' is not.</p>

<p>Here is Greg's original mail describing the system, followed by
some advice about hacking it:</p>

<pre>
   From: Greg Stein &lt;gstein@lyra.org&gt;
   Subject:  new build system (was: Re: CVS update: MODIFIED: ac-helpers ...)
   To: dev@subversion.tigris.org
   Date: Thu, 24 May 2001 07:20:55 -0700
   Message-ID: &lt;20010524072055.F5402@lyra.org&gt;

   On Thu, May 24, 2001 at 01:40:17PM -0000, gstein@tigris.org wrote:
   &gt;   User: gstein
   &gt;   Date: 01/05/24 06:40:17
   &gt;
   &gt;   Modified:    ac-helpers .cvsignore svn-apache.m4
   &gt;   Added:       .        Makefile.in
   &gt;   Log:
   &gt;   Switch over to the new non-recursive build system.
   &gt;...

   Okay... this is it. We're now on the build system.

       "It works on my machine."

   I suspect there may be some tweaks to make on different OSs. I'd be
   interested to hear if Ben can really build with normal BSD make. It
   should be possible.

   The code supports building, installation, checking, and
   dependencies. It does *NOT* yet deal with the doc/ subdirectory. That
   is next; I figured this could be rolled out and get the kinks worked
   out while I do the doc/ stuff.  Oh, it doesn't build Neon or APR yet
   either. I also saw a problem where libsvn_fs wasn't getting built
   before linking one of the test proggies (see below).

   Basic operation: same as before.

   $ ./autogen.sh
   $ ./configure OPTIONS
   $ make
   $ make check
   $ make install

   There are some "make check" scripts that need to be fixed up. That'll
   happen RSN. Some of them create their own log, rather than spewing to
   stdout (where the top-level make will place the output into
   [TOP]/tests.log).

   The old Makefile.am files are still around, but I'll be tossing those
   along with a bunch of tweaks to all the .cvsignore files. There are a
   few other cleanups, too. But that can happen as a step two.

   [ $ cvs rm -f `find . -name Makefile.rm`

     See the mistake in that line? I didn't when I typed it. The find
     returned nothing, so cvs rm -f proceeded to delete my entire
     tree. And the -f made sure to delete all my source files, too. Good
     fugging thing that I had my mods in some Emacs buffers, or I'd be
     bitching.

     I am *so* glad that Ben coded SVN to *not* delete locally modified
     files *and* that we have an "undel" command. I had to go and tweak a
     bazillion Entries files to undo the delete...
   ]

   The top-level make has a number of shortcuts in it (well, actually in
   build-outputs.mk):

   $ make subversion/libsvn_fs/libsvn_fs.la

   or

   $ make libsvn_fs

   The two are the same. So... when your test proggie fails to link
   because libsvn_fs isn't around, just run "make libsvn_fs" to build it
   immediately, then go back to the regular "make".

   Note that the system still conditionally builds the FS stuff based
   on whether DB (See 'Building on Unix' below) is available, and
   mod_dav_svn if Apache is available.

   Handy hint: if you don't like dependencies, then you can do:

   $ ./autogen.sh -s

   That will skip the dependency generation that goes into
   build-outputs.mk. It makes the script run quite a bit faster (48 secs
   vs 2 secs on my poor little Pentium 120).

   Note that if you change build.conf, you can simply run:

   $ ./gen-make.py build.conf

   to regen build-outputs.mk. You don't have to go back through the whole
   autogen.sh / configure process.

   You should also note that autogen.sh and configure run much faster now
   that we don't have the automake crap. Oh, and our makefiles never
   re-run configure on you out of the blue (gawd, I hated when automake
   did that to me).

   Obviously, there are going to be some tweaky things going on. I also
   think that the "shadow" builds or whatever they're called (different
   source and build dirs) are totally broken. Something tweaky will have
   to happen there.  But, thankfully, we only have one Makefile to deal
   with.

   Note that I arrange things so that we have one generated file
   (build-outputs.mk), and one autoconf-generated file (Makefile from
   .in).  I also tried to shove as much logic/rules into
   Makefile.in. Keeping build-outputs.mk devoid of rules (thus, implying
   gen-make.py devoid of rules in its output generation) manes that
   tweaking rules in Makefile.in is much more approachable to people.

   I think that is about it. Send problems to the dev@ list and/or feel
   free to dig in and fix them yourself. My next steps are mostly
   cleanup. After that, I'm going to toss out our use of libtool and rely
   on APR's libtool setup (no need for us to replicate what APR already
   did).

   Cheers,
   -g

   --
   Greg Stein, http://www.lyra.org/
</pre>

<p>And here is some advice for those changing or testing the
configuration/build system:</p>

<pre>
   From: Karl Fogel &lt;kfogel@collab.net&gt;
   To: dev@subversion.tigris.org
   Subject: when changing build/config stuff, always do this first
   Date: Wed 28 Nov 2001

   Yo everyone: if you change part of the configuration/build system,
   please make sure to clean out any old installed Subversion libs
   *before* you try building with your changes.  If you don't do this,
   your changes may appear to work fine, when in fact they would fail if
   run on a truly pristine system.

   This script demonstrates what I mean by "clean out".  This is
   `/usr/local/cleanup.sh' on my system.  It cleans out the Subversion
   libs (and the installed httpd-2.0 libs, since I'm often reinstalling
   that too):

      #!/bin/sh

      # Take care of libs
      cd /usr/local/lib
      rm -f APRVARS
      rm -f libapr*
      rm -f libexpat*
      rm -f libneon*
      rm -f libsvn*

      # Take care of headers
      cd /usr/local/include
      rm -f apr*
      rm -f svn*
      rm -f neon/*

      # Take care of headers
      cd /usr/local/apache2/lib
      rm -f *

   When someone reports a configuration bug and you're trying to
   reproduce it, run this first. :-)

   The voice of experience,
   -Karl
</pre>

</div>


<div class="h2" id="releasing" title="releasing">
<h2>How to release a distribution tarball</h2>

<p>See <a href="release-process.html">The Subversion Release Procedure</a>.</p>

</div>


<div class="h2" id="release-numbering" title="release-numbering">
<h2>Release numbering, compatibility, and deprecation</h2>

<p>Subversion uses "MAJOR.MINOR.PATCH" release numbers, with the same
guidelines as APR (see <a href="http://apr.apache.org/versioning.html"
>http://apr.apache.org/versioning.html</a>), plus a few extensions,
described later.  The general idea is:</p>

<ol>

<li><p>Upgrading/downgrading between different patch releases in the
       same MAJOR.MINOR line never breaks code.  It may cause bugfixes
       to disappear/reappear, but API signatures and semantics remain
       the same.  (Of course, the semantics may change in the trivial
       ways appropriate for bugfixes, just not in ways that would
       force adjustments in calling code.)</p>
</li>

<li><p>Upgrading to a new minor release in the same major line may
       cause new APIs to appear, but not remove any APIs.  Any code
       written to the old minor number will work with any later minor
       number in that line.  However, downgrading afterwards may not
       work, if new code has been written that takes advantage of the
       new APIs.</p>
</li>

<li><p>When the major number changes, all bets are off.  This is the
       only opportunity for a full reset of the APIs, and while we try
       not to gratuitously remove interfaces, we will use it to clean
       house a bit.</p>
</li>

</ol>

<p>Subversion extends the APR guidelines to cover client/server
compatibility questions:</p>

<ol>

<li><p>A patch or minor number release of a server (or client) never
       breaks compatibility with a client (or server) in the same
       major line.  However, new features offered by the release might
       be unsupported without a corresponding upgrade to the other
       side of the connection.  For updating ra_svn code specifically,
       please observe these principles:</p>

    <ol>

    <li><p>Fields can be added to any tuple; old clients will simply
           ignore them.  (Right now, the marshalling implementation
           does not let you put number or boolean values in the
           optional part of a tuple, but changing that will not affect
           the protocol.)</p>
   
        <p>We can use this mechanism when information is added to an
           API call.</p>
    </li>
   
    <li><p>At connection establishment time, clients and servers exchange
           a list of capability keywords.</p>
   
         <p>We can use this mechanism for more complicated changes,
           like introducing pipelining or removing information from
           API calls.</p>
    </li>
   
    <li><p>New commands can be added; trying to use an unsupported
           command will result in an error which can be checked and dealt
           with.</p>
    </li>
   
    <li><p>The protocol version number can be bumped to allow graceful
           refusal of old clients or servers, or to allow a client or
           server to detect when it has to do things the old way.</p>
 
        <p>This mechanism is a last resort, to be used when capability
           keywords would be too hard to manage.</p>
    </li>

    </ol>
</li>

<li><p>Working copy and repository formats are backward- and
       forward-compatible for all patch releases in the same minor
       series.  They are forward-compatible for all minor releases in
       the same major series; however, a minor release is allowed to
       make a working copy or repository that doesn't work with
       previous minor releases, where "make" could mean "upgrade" as
       well as "create".</p>
</li>

</ol>

<p>Subversion does not use the "even==stable, odd==unstable"
convention; any unqualified triplet indicates a stable release:</p>

<pre>
   1.0.1  --&gt;  first stable patch release of 1.0
   1.1.0  --&gt;  next stable minor release of 1.x after 1.0.x
   1.1.1  --&gt;  first stable patch release of 1.1.x
   1.1.2  --&gt;  second stable patch release of 1.1.x
   1.2.0  --&gt;  next stable minor release after that
</pre>

<p>The order of releases is semi-nonlinear&nbsp;&mdash;&nbsp;a 1.0.3
<em>might</em> come out after a 1.1.0.  But it's only "semi"-nonlinear
because eventually we declare a patch line defunct and tell people to
upgrade to the next minor release, so over the long run the numbering
is basically linear.</p>

<p>Non-stable releases are qualified with "alphaN" or "betaN"
suffixes, and release candidates with "-rcN".  For example, the
prereleases leading to 1.3.7 might look like this:</p>

<pre>
   subversion-1.3.7-alpha1.tar.gz
   subversion-1.3.7-alpha2.tar.gz
   subversion-1.3.7-beta1.tar.gz
   subversion-1.3.7-rc1.tar.gz
   subversion-1.3.7-rc2.tar.gz
   subversion-1.3.7-rc3.tar.gz
   subversion-1.3.7.tar.gz
</pre>

<p>The output of 'svn --version' corresponds in the obvious way:</p>

<pre>
   version 1.3.7 (Alpha 1)
   version 1.3.7 (Alpha 2)
   version 1.3.7 (Beta 1)
   version 1.3.7 (Release Candidate 1)
   version 1.3.7 (Release Candidate 2)
   version 1.3.7 (Release Candidate 3)
   version 1.3.7
</pre>

<p>(See <a href="#alphas-betas">this section</a> for more information
about when and how we do alpha and beta releases.)</p>

<p>When you 'make install' subversion-1.3.7-rc1, it still installs as
though it were "1.3.7", of course.  The qualifiers are metadata on the
release; we want each subsequent prerelease release to overwrite the
previous one, and the final release to overwrite the last
prerelease.</p>

<p>For working copy builds, there is no tarball name to worry about,
but 'svn --version' still produces special output:</p>

<pre>
   version 1.3.8 (dev build)
</pre>

<p>The version number is the next version that project is working
towards.  The important thing is to say "dev build".  This indicates
that the build came from a working copy, which is useful in bug
reports.</p>

<p>We have no mechanism for releasing dated snapshots.  If we want
code to get wider distribution than just those who build from working
copies, we put out a prerelease.</p>

<div class="h3" id="name-reuse" title="name-reuse">
<h3>Reuse of release names</h3>

<p>If a release or candidate release needs to be quickly re-issued due
to some non-code problem (say, a packaging glitch), it's okay to reuse
the same name, as long as the tarball hasn't been
<a href="#tarball-signing">blessed by signing</a> yet.  But if it has
been uploaded to the standard distribution area with signatures, or if
the re-issue was due to a change in code a user might run, then the
old name must be tossed and the next name used.</p>

</div>

<div class="h3" id="deprecation" title="deprecation">
<h3>Deprecation</h3>

<p>When a new, improved version of an API is introduced, the old one
remains for compatibility, at least until the next major release.
However, we mark the old one as deprecated and point to the new one,
so people know to write to the new API if at all possible.  When
deprecating, mention the release after which the deprecation was
introduced, and point to the new API.  If possible, replace the old
API documentation with a diff to the new one.  For example:</p>

<pre>
   /**
    * @deprecated Provided for backward compatibility with the 1.0.0 API.
    *
    * Similar to svn_repos_dump_fs2(), but with the @a use_deltas
    * parameter always set to @c FALSE.
    */
   svn_error_t *svn_repos_dump_fs(svn_repos_t *repos,
                                  svn_stream_t *dumpstream,
                                  svn_stream_t *feedback_stream,
                                  svn_revnum_t start_rev,
                                  svn_revnum_t end_rev,
                                  svn_boolean_t incremental,
                                  svn_cancel_func_t cancel_func,
                                  void *cancel_baton,
                                  apr_pool_t *pool);
</pre>

<p>When the major release number changes, the "best" new API in a
series generally replaces all the previous ones (assuming it subsumes
their functionality), and it will take the name of the original API.
Thus, marking 'svn_repos_dump_fs' as deprecated in 1.1.x doesn't mean
that 2.0.0 doesn't have 'svn_repos_dump_fs', it just means the
function's signature will be different: it will have the signature
held by svn_repos_dump_fs2 (or svn_repos_dump_fs3, or whatever) in
1.1.x.  The numbered-suffix names disappear, and there is a single
(shiny, new) svn_repos_dump_fs again.</p>

<p>One exception to this replacement strategy is when the old function
has a totally unsatisfying name anyway.  Deprecation is a chance to
fix that: we give the new API a totally new name, mark the old API as
deprecated, point to the new API; then at the major version change, we
remove the old API, but don't rename the new one to the old name,
because its new name is fine.</p>
   
</div>

</div>


<div class="h2" id="release-stabilization" title="release-stabilization">
<h2>Stabilizing and maintaining releases</h2>

<p>Minor and major number releases go through a stabilization period
before release, and remain in maintenance (bugfix) mode after release.
To start the release process, we create an "A.B.x" branch based on the
latest trunk, for example:</p>

<pre>
   $ svn cp http://svn.collab.net/repos/svn/trunk \
            http://svn.collab.net/repos/svn/branches/A.B.x
</pre>

<p>The stabilization period for a new A.B.0 release normally lasts
four weeks, and allows us to make conservative bugfixes and discover
showstopper issues.  The stabilization period begins with a release
candidate tarball with the version A.B.0-rc1.  Further release
candidate tarballs may be made as blocking bugs are fixed; for
example, if a set of language bindings is found to be broken, it is
prudent to make a new release candidate when they are fixed so that
those language bindings may be tested.</p>

<p>At the beginning of the final week of the stabilization period, a
new release candidate tarball should be made if there are any changes
pending since the last one.  The final week of the stabilization
period is reserved for critical bugfixes; fixes for minor bugs should
be deferred to the A.B.1 release.  A critical bug is a non-edge-case
crash, a data corruption problem, a major security hole, or something
equally serious.</p>

<p>Under some circumstances, the stabilization period will be
extended:</p>

<ul>
<li><p>If a potentially destabilizing change must be made in order to
       fix a bug, the entire four-week stabilization period is
       restarted.  A potentially destabilizing change is one which
       could affect many parts of Subversion in unpredictable ways, or
       which involves adding a substantial amount of new code.  Any
       incompatible API change (only allowable in the first place if
       the new release is an A.0.0 release) should be considered a
       potentially destabilizing change.</p>
</li>

<li><p>If a critical bugfix is made during the final week of the
       stabilization period, the final week is restarted.  The final
       A.B.0 release is always identical to the release candidate made
       one week before (with the exceptions discussed below).</p>
</li>

</ul>

<p>If there are disagreements over whether a change is potentially
destabilizing or over whether a bug is critical, they may be settled
with a committer vote.</p>

<p>After the A.B.0 release is out, patch releases (A.B.1, A.B.2, etc.)
follow when bugfixes warrant them.  Patch releases do not require a
four week soak, because only conservative changes go into the
line.</p>

<p>Certain kinds of commits can go into A.B.0 without restarting the
soak period, or into a later release without affecting the testing
schedule or release date:</p>

<ul>
<li><p>Without voting:</p>
    <ul>
    <li><p>Changes to the STATUS file.</p></li>
    <li><p>Documentation fixes.</p></li>
    <li><p>Changes that are a normal part of release bookkeeping, for
           example, the steps listed in notes/releases.txt.</p></li>
    <li><p>Changes to dist.sh by, or approved by, the release manager.</p></li>
    <li><p>Changes to message translations in .po files or additions of
           new .po files.</p></li>
    </ul>
</li>

<li><p>With voting:</p>
    <ul>
    <li><p>Anything affecting only tools/, packages/, or bindings/.</p></li>
    <li><p>Changes to printed output, such as error and usage messages, as
           long as format string "%" codes and their args are not
           touched.</p></li>
    </ul>
</li>

</ul>

<p>NOTE: The requirements on message translation changes are looser
than for text messages in C code.  Changing format specifiers in .po
files is allowed because their validity can be checked mechanically
(with the -c flag on msgfmt of GNU gettext).  This is done at build
time if GNU gettext is in use.</p>

<p>Core code changes, of course, require voting, and restart the soak
or test period, since otherwise the change could be undertested.</p>

<p>The voting system works like this:</p>

<p>A change to the A.B.x line must be first proposed in the
A.B.x/STATUS file.  Each proposal consists of a short identifying
block (e.g., the revision number of a trunk or related-line commit, or
perhaps an issue number), a brief description of the change, an
at-most-one-line justification of why it should be in A.B.x, perhaps
some notes/concerns, and finally the votes.  The notes and concerns
are meant to be brief summaries to help a reader get oriented; please
don't use the STATUS file for actual discussion, use dev@ instead.</p>

<p>Here's an example, probably as complex as an entry would ever
get:</p>

<pre>
   * r98765 (issue #56789)
     Make commit editor take a closure object for future mindreading.
     Justification: API stability, as prep for future enhancement.
     Notes: There was consensus on the desirability of this feature in
       the near future; see thread at http://... (Message-Id: blahblah).
     Concerns: Vetoed by jerenkrantz due to privacy concerns with the
       implementation; see thread at http://... (Message-Id: blahblah)
     Votes:
       +1: ghudson, bliss
       +0: cmpilato
       -0: gstein
       -1: jerenkrantz
</pre>

<p>A change needs three +1 votes from full committers (or partial
committers for the involved areas), and no vetoes, to go into
A.B.x.</p>

<p>If you cast a veto (i.e. -1), please state the reason in the
concerns field, and include a url / message-id for the list discussion
if any.  You can go back and add the link later if the thread isn't
available at the time you commit the veto.</p>

<p>If you add revisions to a group, note that the previous voters have
not voted for those revisions, as follows:</p>

<pre>
   * r30643, r30653, r30785
     Update bash completion script.
     Votes:
       +1: arfrever (r30785 only), stylesen
</pre>

<p>If in case votes have been communicated via IRC or other means,
note that in the log message. <a href="#obvious-fix">Obvious fixes</a>
do not require '(rX only)' to be mentioned.</p>

<p>Voting +1 on a change doesn't just mean you approve of it in
principle.  It means you have thoroughly reviewed the change, and find
it correct and as nondisruptive as possible.  When it is committed to
the release branch, the log message will include the names of all who
voted for it, as well as the original author and the person making the
commit.  All of these people are considered equally answerable for
bugs.</p>

<p>If you've reviewed a patch, and like it but have some reservations,
you can write "+1 (concept)" and then ask questions on the list about
your concerns.  You can write "+0" if you like the general idea but
haven't reviewed the patch carefully.  Neither of these votes counts
toward the total, but they can be useful for tracking down people who
are following the change and might be willing to spend more time on
it.</p>

<p>There is a somewhat looser voting system for areas that are not
core code, and that may have fewer experts available to review changes
(for example, tools/, packages/, bindings/, test scripts, etc.).  A
change in these areas can go in with a +1 from a full committer or a
partial committer for that area, at least one +0 or "concept +1" from
any other committer, and no vetoes.  (If a change affects the build
system, however, it is considered a core change, and needs three
+1's.)  Use your judgment and don't review changes unless you have
some competence to do so, of course.  The goal is to get at least two
pairs of eyes on the change, without demanding that every reviewer
have the same amount of expertise as the area maintainer.  This way
one can review for general sanity, accurate comments, obvious
mistakes, etc, without being forced to assert "Yes, I understand these
changes in every detail and have tested them."</p>

<p>Before proposing a change in STATUS, you should try merging it onto
the branch to ensure that it doesn't produce merge conflicts.  If
conflicts occur, please create a new temporary branch from the release
branch with your changes merged and the conflicts resolved.  The
branch should be named A.B.x-rYYYY, where YYYY is the first revision
of your change in the STATUS file.  Add a note in the STATUS file
about the existence of the temporary branch.  If the change involves
further work, you can merge those revisions to the branch.  When the
entry for this change is removed from STATUS, this temporary branch
should also be removed to avoid cluttering the /branches
directory.</p>

<p>NOTE: Changes to STATUS regarding the temporary branch, including
voting, are always kept on the main release branch.</p>

<div class="h3" id="alphas-betas" title="alphas-betas">
<h2>Alpha and beta releases</h2>

<p>When we want new features to get wide testing before we enter the
formal stabilization period described above, we'll sometimes release
alpha and beta tarballs (as <a href="#release-numbering" >shown
earlier</a>).  The line between alpha and beta is fuzzy, but
basically, "alpha" means "we know or expect there are problems", and
"beta" means "we don't expect problems".  There is no requirement to
do any beta releases even if there were "alpha1", "alpha2", etc
releases; we could just jump straight to "rc1".  However, there are
circumstances where a beta can be useful: for example, if we're unsure
of a UI decision and want to get wider user feedback before
solidifying it into a formal release candidate.</p>

<p>Alphas and betas are only for people who want to help test, and who
understand that there may be UI- or API-incompatible changes before
the final release.  The signature requirements are at the release
manager's discretion.  Typically, the RM will require only 1 or 2
signatures for each platform, and to tell signers that they can still
sign even if their testing reveals minor failures, as long as they
think the code is solid enough to make testing by others worthwhile.
The RM should request that signers include a description of any errors
along with their signatures, so the problems can be published when the
alpha or beta release is announced.</p>

<p>When the alpha or beta is publicly announced, distribution packagers
should be firmly warned off packaging it.  See <a
href="http://subversion.tigris.org/servlets/ReadMsg?list=announce&amp;msgNo=264"
>this mail from Hyrum K. Wright</a> for a good model.</p>

</div>

</div>

<div class="h2" id="tarball-signing">
<h2>Signing source distribution packages (a.k.a tarballs)</h2>

<p>Before a release or release candidate is officially made public, it is
made available in a temporary location for committers to test and sign.
The point is to have the tarballs tested on more systems than just that of the
person who rolled the release.  When there are three signatures from full
committers for each of the <tt>.tar.bz2</tt>, <tt>.tar.gz</tt> and
<tt>.zip</tt> files, the release (candidate) can go public.</p>

<p>Signing a tarball means that you assert certain things about it.  When
sending your signature (see below), indicate in the mail what steps
you've taken to verify that the tarball is correct.  Running
<tt>make check</tt> over all RA layers and FS backends is a good idea,
as well as building and testing the bindings.</p>

<p>After having extracted and tested the tarball, you should sign it using
<a href="http://www.gnupg.org">gpg</a>.  To do so, use a command like:</p>

<pre>
    gpg -ba subversion-1.3.0-rc4.tar.bz2
</pre>

<p>This will result in a file with the same name as the signed file, but with
a <tt>.asc</tt> extension in the appropriate format for inclusion in the
release announcement.  Include this file in a mail, typically in reply
to the announcement of the unofficial tarball.</p>

<p>If you've downloaded and tested a <tt>.tar.bz2</tt> file, it is possible to
sign a <tt>.tar.gz</tt> file with the same contents without having
to download and test it separately.  The trick is to extract the
<tt>.bz2</tt> file, and pack it using <tt>gzip</tt> like this:</p>

<pre>
    bzip2 -cd subversion-1.3.0-rc4.tar.bz2 \
    | gzip -9n &gt; subversion-1.3.0-rc4.tar.gz
</pre>

<p>The resulting file should be identical to the file generated by the
release manager, and thus can be signed as described above.
To verify that the files are identical, you may use either the MD5 checksums
or the release manager's signature, both of which should be provided with the
tarballs.
</p>
</div>

<div class="h2" id="l10n" title="l10n">
<h2>Localization (l10n)</h2>

<p>Translation has been divided into two domains.  First, there is the
translation of server messages sent to connecting clients.  This issue
has been <a
href="http://svn.collab.net/repos/svn/trunk/notes/l10n-problems">punted
for now</a>.  Second there is the translation of the client and its
libraries.</p>

<p>The gettext package provides services for translating messages.  It
uses the xgettext tool to extract strings from the sources for
translation.  This works by extracting the arguments of the _() and
N_() macros.  The former is used in context where function calls are
allowed (typically anything except static initializers).  The latter
is used whenever _() isn't.  Strings marked with N_() need to be
passed to gettext translation routines whenever referenced in the
code.  For an example, look at how the header and footer are handled
in subversion/svn/help-cmd.c.</p>

<p>When using direct calls to gettext routines (*gettext or
*dgettext), keep in mind that most of Subversion code is library code.
Therefore the default domain is not necessarily Subversion's own
domain.  In library code you should use the dgettext versions of the
gettext functions.  The domain name is defined in the PACKAGE_NAME
define.</p>

<p>All required setup for localization is controlled by the ENABLE_NLS
conditional in svn_private_config.h (for *nix) and
svn_private_config.hw (for Windows).  Be sure to put</p>

<pre>
   #include "svn_private_config.h"
</pre>

<p>as the last include in any file which requires localization.</p>

<p>Also note that return values of _() and *gettext() calls are UTF-8
encoded; this means that they should be translated to the current
locale being written as any form of program output.</p>

<p>The GNU gettext manual
(<a
href="http://www.gnu.org/software/gettext/manual/html_node/gettext_toc.html"
>http://www.gnu.org/software/gettext/manual/html_node/gettext_toc.html</a>)
provides additional information on writing translatable programs in
its section "Preparing Program Sources".  Its hints mainly apply to
string composition.</p>

<p>Currently available translations can be found in <a
href="http://svn.collab.net/repos/svn/trunk/subversion/po/" >the po
section of the repository</a>.  Please contact
dev@subversion.tigris.org when you want to start a translation not
available yet.  Translation discussion takes place both on that list
and on dedicated native language mailing lists (<a
href="http://subversion.tigris.org/servlets/ProjectMailingListList"
><em>l10n-??@subversion.tigris.org</em></a>).</p>

<p>See <a href="http://svn.collab.net/repos/svn/trunk/TRANSLATING"
>http://svn.collab.net/repos/svn/trunk/TRANSLATING</a> for more
information about translating.</p>

</div>

</div>
</body>
</html>
